form htmlservice issue with passing parameters - google-apps-script

I have looked at other posts and am struggling with the same issue of how to pass form values to GAS. Since GUI editor has been depreciated, I decided to go the htmlservice form route. In this simple code below, I'm just trying to access the form field aField and paste it in a cell.
function doGet() {
return HtmlService.createHtmlOutputFromFile('myForm.html');
}
function processForm(value) {
var myRange = SpreadsheetApp.openById("xxx").getSheetByName("Results-List").getDataRange("B20");
myRange.setValue(value.aField);
}
This is the HTML template myForm.html:
<html>
<form id="myForm">
<input name="aField" id="aField">
<input type="button" id="submit" value="submit" onclick = "sendData()">
</form>
<script>
function sendData() {
google.script.run.processForm(document.getElementById("myForm"));
}
</script>
</html>

.getDataRange() has a different use. Use .getRange('B20') to get this to work.

Related

Goole App Script HTML Onclick Button Issue

I am working on a sidebar menu, and so far, I got it working to load values from a sheet. I am working on getting the sidebar to update the value that is selected and currently struggling a little bit since I can't seem to catch an error and struggling a bit to figure out how to error handle app scripts and HTML in app scripts. So the first ask here is can someone point me to a reference on how to error handle issues with HTML and Apps script.
My second issue my current problem, I have an HTML button that is calling onFormUpdate() function which is located inside a seperate HTML file just for handling javascript functions. Here is my index.html
<!DOCTYPE html>
<html>
<base target="_top">
<?!= include('sidebar/sidebarcss'); ?>
<body>
<div id="title">
<div><h2>Meeting Item</h2></div>
</div>
<div id="meet">
<form id="meetform">
Row ID: <br>
<input type="text" id="meetrowId" name="meetrowId" maxlength="4" size="4" readonly />
Meet ID: <br>
<input type="text" id="meetId" name="meetId" maxlength="8" size="10" readonly />
Enter Date: <br>
<input type="text" id="meetingDate" name="meetingDate"/>
Topic: <br>
<input type="text" id="meetTopic" name="meetTopic"/>
Agenda: <br>
<textarea name="meetAgenda" id="meetAgenda" rows="10" cols="30"></textarea>
Comment History:<br>
<textarea name="meetComments" id="meetComments" rows="10" cols="30" readonly ></textarea>
Add Comments:<br>
<textarea name="meetComUpdate" id="meetComUpdate" rows="10" cols="30"></textarea><br>
<input type="button" class="button" value="Update Record" onclick="onFormUpdate()"/>
</form>
</div>
<input type="button" class="button" value="Select Another ID" onclick="google.script.run.showEmailSidebar()"/>
<input type="button" class="button" value="Close" onclick="google.script.host.close()"/>
<?!= include('sidebar/script'); ?>
<script>
google.script.run.withSuccessHandler(onSuccess).SidebarActiveRow();
</script>
</body>
</html>
Within the form i call my onFormUpdate() and nothing happens when I click the button. As you can see i include 'sidebar/script' which is my html file that stores all javascript functions. OnFormUpdate() is located within the 'sidebar/script' and is shown below:
<script>
function onSuccess([cellrow, meetid, meetdate, meettopic, meetagenda, meetcomments])
{
/*
Secondary method for string parsing
const table =sidebarVar.split(",") //["key:value","key:value"]
.map(pair => pair.split(":")); //[["key","value"],["key","value"]]
const result = Object.fromEntries(table);
result.meetid;
*/
document.getElementById('meetrowId').value = cellrow
document.getElementById('meetId').value = meetid
document.getElementById('meetingDate').value = meetdate
document.getElementById('meetTopic').value = meettopic
document.getElementById('meetAgenda').value = meetagenda;
document.getElementById('meetComments').value = meetcomments;
}
function onFormUpdate()
{
var recordform =
{
row: document.getElementById('meetrowId').value,
topic: document.getElementById('meetTopic').value,
agenda: document.getElementById('meetAgenda').value,
newcomment: document.getElementById('meetComUpdate').value
};
google.script.run.withSuccessHandler(SidebarActiveRow).recordUpdate(recordform);
}
</script>
As you can see I am trying to get the app handler to call SidebarActiveRow which is leverage with my onSuccess function to load data elements from the sheet; this works fine. The handler is calling SidebarActiveRow to run after i successfully run recordUpdate() which is located in my code.gs file. So far nothing is happening. I have this current code for testing to see if the function works but no success.
function recordUpdate(recordform) {
SpreadsheetApp.getUi().alert(recordform.row);
}
I get no prompts which I can't seem to troubleshoot since the html and Apps Script function don't really show an errors. I did go to executions to see if there were any errors and i don't see any at this time. So i am looking for some help here.
Look at javascript Error Handling.
For HTML:
<script>
function someFunction() {
try {
// ... do some code
}
catch(err) {
alert(err);
}
}
</script>
For App Script
function someFunction() {
try {
// ... do some code
}
catch(err) {
Logger.log(err); // check execution log
}
}

Submit a form with a hidden field that is a var from a calculation

I have a var that is the result of a math calculation which changes based on the users input. I'm trying to create a get-form in html that will send the results to a new page.
my result appears on the page as var newpayment
document.querySelectorAll("#cart span.newpayment")[0].innerHTML = newpayment.toFixed(2);
However the form does not show the result..
<form id="form1" method="get">
<input type="hidden" id="submit_total1" value=""
name="submit_total1">
<button id="select1">Select</button>
</form>
<script>
var form = document.getElementById("form1");
document.getElementById("form1").action = "/complete/file.php";
document.getElementById("select1").addEventListener("click", function ()
{
var submit_total1=newpayment;
form.submit();
});
</script>
Thanks for your help but I'm still not able to get it to work. I've also tried another method by setting the value after the form is rendered (see Below). The var "newpayment" is displayed on the page as the span class but it will not populate as the value in the form. Thank you in advance for any remedies.
`
<script>
function setVal(){
document.getElementById('id').value=newpayment;
}
</script>
<p >$<span class="newpayment">0.00</span></p>`

Prevent HTML Page Refresh

At this stage I'm mostly used to backend Javascript and server side Java, so my HTML is not as savvy as it needs to be.
I've built several applications that require user input with Apps script, but I was using the now deprecated UI service, as I'm not a designer and this provided an easy way to design simple pages to pass data back and forth. With the UI service having been deprecated for some time, I'm begging the arduous task of migrating these services to the HTML service, and I'm noticing some difference in behavior.
For example, when submitting a form, the entire page refreshes to a blank page, and I can't seem to prevent that. The UI service would remain static for information re-entry, and I can't find a working method to get the HTML service to either stop refreshing or reload the form.
Simple code to reproduce my issue:
function doGet() {
return HtmlService.createHtmlOutputFromFile('test')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function logValues(value){
Logger.log('Something worked....');
}
With the index file being:
<form>
<input type="submit" value="Book Meeting" onclick="google.script.run
.logValues()">
</form>
Some things I've tried:
1) Adding a callback to the 'doGet' function, to attempt to get the page to load again.
2) Adding a whole new function to try and call a NEW HTML page.
The issue here is my poor understanding of the HTML service, but is there a simple way for me to just clear the form for re-submission, or alternatively just reload the page? None of the other questions I've found on SO adequately answer this question in a way I can understand.
Since you're technically submitting your form by clicking the submit button, then that creates the page refresh. You need to cancel the submit event with the preventDefault function, which "Cancels the event if it is cancelable, without stopping further propagation of the event."
See the docs here: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
So maybe you can try something along these lines (straight from the docs):
function stopDefAction(evt) {
evt.preventDefault();
}
document.getElementById('my-checkbox').addEventListener('click', stopDefAction, false);
Another option is to remove the form/input elements and simply use a button element instead, which doesn't trigger a page refresh on click.
It's an interesting ride switching old UI services across, I just did that with one of my applications and it has really improved the readability of the code. I posted a copy of a basic version of what I was doing in another question
Once you get your head around it all it becomes a lot simpler. This is a really basic example of using multiple HTML files similar to your example using the HTMLService when submitting forms (you can pass in parameters instead)
Code.gs
function doGet() {
return HtmlService.createTemplateFromFile('Main')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
}
function onLogin(form) {
if (form.username == "fuzzyjulz") {
var template = HtmlService.createTemplateFromFile('Response');
//Setup any variables that should be used in the page
template.firstName = "Fuzzy";
template.username = form.username;
return template.evaluate()
.setSandboxMode(HtmlService.SandboxMode.NATIVE)
.getContent();
} else {
throw "You could not be found in the database please try again.";
}
}
function include(filename) {
return HtmlService.createTemplateFromFile(filename)
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.getContent();
}
Main.html
<?!= include('CSS'); ?>
<script>
function loadPage(htmlOut) {
var div = document.getElementById('content');
div.innerHTML = htmlOut;
document.getElementById('errors').innerHTML = "";
}
function onFailure(error) {
var errors = document.getElementById('errors');
errors.innerHTML = error.message;
}
</script>
<div id="errors"></div>
<div id="content">
<?!= include('Login'); ?>
</div>
CSS.html
<style>
p b {
width: 100px;
display: inline-block;
}
</style>
Login.html
<script>
function onLoginFailure(error) {
var loginBtn = document.getElementById('loginBtn');
loginBtn.disabled = false;
loginBtn.value = 'Login';
onFailure(error);
}
</script>
<div class="loginPanel">
<form>
<p>
<b>Username: </b>
<input type="text" name="username"/>
</p>
<input type="button" id="loginBtn" value="Login" onclick="this.disabled = true; this.value = 'Loading...';google.script.run
.withSuccessHandler(loadPage)
.withFailureHandler(onLoginFailure)
.onLogin(this.parentNode)"/>
</form>
</div>
Response.html
<div class="text">
Hi <?= firstName ?>,<br/>
Thanks for logging in as <?= username ?>
</div>

HtmlService form submit opens new tab with foo bar URL

I am attempting to build a UI for a spreadsheet using GAS HtmlService. The HTML below is a very simple form with a single text box that pulls a value ("Kristina") from the sheet, successfully. However, when I try to submit the form a new tab is opened in Chrome that attempts to open the URL "bffc95ee-ff64-4d2c-xxxx-19d9824eb4b4.foo.bar/?fname=Kristina" with "xxxx" replacing more random letters and numbers (just in case). At no point do I use the words "foo.bar" in my code, so I'm pretty sure that that part isn't coming from me. It does not change each time or after logging out and back in. I'm getting the same result on two different computers.
<html>
<body>
<div>
<form id="formtest1">
<label>First Name</label>
<input name="fname" type="text" maxlength="255" value="<?= fname ?>"/>
<input type="submit" value="Submit"
onclick="google.script.run.processForm(document.getElementById('formtest1'));
google.script.host.close()"/>
</form>
</div>
</body>
</html>
The above is being displayed using the following function:
function htmltest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht = ss.getActiveSheet();
var html = HtmlService.createTemplateFromFile("HTML");
html.fname = sht.getRange(2, 3).getValue();
ss.show(html.evaluate());
};
If I understand correctly, the "google.script.run.processForm(...)" script in the HTML should trigger the following function, as set up in the projects triggers:
function onFormSubmit(){
Browser.msgBox("Test");
};
But it doesn't appear to do so as the form doesn't close and the msgBox doesn't appear. Only the foo bar URL in a new tab.
Hopefully I've explained the issue clearly and am not making an embarrassing mistake.
You cannot use a real "submit" button with google.script.run (this is a documented restriction in the user guide). Change it to "button" and it should work fine.
The project trigger onFormSubmit() will be triggered by a submission via the Forms Service. There is no relationship between this trigger and your HTML code; they are two different ways to interact with users.
An html forms pattern is shown in the HTML Service documentation here, and the script below is an adaptation of it.
Code.gs
The only real change from your original is that onFormSubmit() has been replaced with processForm(form), which includes a parameter, for the object that will be passed from the html code.
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "htmltest",
functionName : "htmltest"
}];
sheet.addMenu("Custom Menu", entries);
};
function htmltest(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht = ss.getActiveSheet();
var html = HtmlService.createTemplateFromFile("HTML");
html.fname = sht.getRange(2, 3).getValue();
//Logger.log( html.getCodeWithComments() );
ss.show(html.evaluate());
};
function processForm(form){
var fname = form.fname;
Browser.msgBox("Test - " + fname);
};
HTML.html
This is a modification of your original, echoing the pattern from the documentation. The form submission SuccessHandler is a one-liner, which closes the dialog. Once it completes, the server-side function is invoked with the form content, retrieved using this.parentNode (to refer to the form).
There are other ways - see Get value of html text box in Apps Script function for a different approach.
<html>
<script type="text/javascript">
// SuccessHandler to close form
function close() {google.script.host.close();}
</script>
<body>
<div>
<form>
<label>First Name</label>
<input name="fname" type="text" maxlength="255" value="<?= fname ?>"/>
<input type="button" value="Submit" onclick="google.script.run
.withSuccessHandler(close)
.processForm(this.parentNode)"/>
</form>
</div>
</body>
</html>
Just add this to your script tag on your html file.
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
Source: HTML Service: Communicate with Server Functions

Accessing object is Google App Script

I am actually following the HtmlService documentation (https://developers.google.com/apps-script/html_service) in creating a form. I notice there is this part where it says it will be an object after user submit the form and the structure will be like this:
{ myFile: <a Blob containing the file>;
aField: <the value in the field> }
Can I know how can I access to those object in Google App Script?
In your server code:
function processForm(theForm) {
Logger.log(theForm.aField);
Logger.log(theForm.myFile.getName());
}
In your HTML:
<form id='myForm'>
<input name='myFile' type='file'>
<input name='aField'>
</form>
<script>
google.script.run.processForm(document.getElementById('myForm'));
</script>