Google Apps Script with HTML Forms - html

I am working on a google add-on which uses an HTML sidebar. The sidebar has an HTML form with checkboxes. I want some of the checkboxes to be checked when the user opens the sidebar, based on some User Properties that will already have been initialized. (When the form submits, the Properties are updated. They all start as on). Here is the code for the sidebar:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<title>Settings</title>
<script>
function onSettingsOpen()
{
Logger.log("In the script");
console.log("In the script");
document.getElementById(propsList[0]).checked = (allPreferences.getProperty(propsList[0]) === "true");
document.getElementById(propsList[1]).checked = (allPreferences.getProperty(propsList[1]) === "true");
document.getElementById(propsList[2]).checked = (allPreferences.getProperty(propsList[2]) === "true");
document.getElementById(propsList[3]).checked = (allPreferences.getProperty(propsList[3]) === "true");
document.getElementById(propsList[4]).checked = (allPreferences.getProperty(propsList[4]) === "true");
document.getElementById(propsList[5]).checked = (allPreferences.getProperty(propsList[5]) === "true");
}
</script>
</head>
<body onload="onSettingsOpen()">
<form id="baseSettings" onsubmit="event.preventDefault(); google.script.run.processForm(this)">
<h2>What settings would you like to be on? Please select all that apply.</h2>
<br>
<input type="checkbox" name="checks" value="spaces" id="spaces">Double spaces
<br>
<input type="checkbox" name="checks" value="punctuation" id="punctuation">Punctuation outside of quotes
<br>
<input type="checkbox" name="checks" value="caps" id="caps">Capitilazation at beginning of sentences
<br>
<input type="checkbox" name="checks" value="contractions" id="contractions">Contractions
<br>
<input type="checkbox" name="checks" value="numbers" id="numbers">Small numbers
<br>
<input type="checkbox" name="checks" value="repWords" id="repWords">Repeated words
<br>
<br>
<input type="submit" value="Submit">
</form>
</body>
So as far as I can tell, the Logger.logs and Console.logs aren't running, which implies that the function just isn't running. However, I could not find documentation on running Console/Logger Log functions in an HTML script file; I'm not sure if that is actually the telling factor. What I can't figure out is where to run the function so that it can actually effect the checkboxes. I fear that running it onload of the body won't actually do anything to the checkboxes- it would have to run within the form itself. Where should I call the function?
Here is my create settings pane function:
function openSettings ()
{
populateData ();
initializePreferences();
Logger.log("Data is initialized; pref 1 = " +
allPreferences.getProperty(propsList[0]));
var htmlOutput = HtmlService
.createHtmlOutputFromFile('Settings.html')
.setWidth(300)
.setTitle("Settings");
DocumentApp.getUi().showSidebar(htmlOutput);
}
Any help appreciated!

You could use a function like this when you load the sidebar
<script>
window.onload=function(){
google.script.run
.withSuccessHandler(function(data){
//initialize your checkboxes here
})
.getPropertData();//go to server side to get access to PropertiesService data
};
Client to Server Communication

Your onSettingsOpen references propsList, but this is undefined. You should pass the data in the function by giving the function an argument, e.g. onSettingsOpen(preferences).
Assuming you are storing these preferences in some PropertiesService, when you call Properties.getProperties() you get back an object with key, value pairs. If you make these match your HTML input "id" attributes, you can just lookup the values in the object by passing the id as a key.
Inside the sidebar:
<script>
google.script.run.withSuccessHandler(onSettingsOpen).getAllPreferences();
// #param {Object} preferences - key, value pairs from Properties.getProperties()
function onSettingsOpen(preferences)
{
console.log("In the script");
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
for (let i = 0; i < checkboxes.length; ++i) {
checkboxes[i].checked = preferences[checkboxes[i].id];
}
}
</script>
Server-side code would need the appropriate getAllPreferences function:
function getAllPreferences() {
return PropertiesService.getUserProperties().getProperties();
}

Related

Appending access code input into URL as utm tag

I'm currently building a landing page with an access code form field.
I'm stuck on finding a way to get the access code entered into a form to be appended as a tag on the url.
Enter "12345" into field and on submit direct to url "www.website.com/?code=12345"
Below is the code I have so far - :
<script>
function btntest_onclick(){
if (document.getElementById('input-code').value == '1234','5678','9809') {
var domain = "http://www.website.com?";
var data = $(this).serialize();
window.location.href = url
}
else {
alert ( 'not found' );
}
};
</script>
<center>
<span class="text-container">
<input type="text" name="accesscode" placeholder="ACCESS CODE" maxlength="10" size="25" id="input-code">
<p>ENTER</p>
</span>
</center>
Any advice on this would be greatly appreciated!
Thanks.
You have a few problems in the code:
The if containing document.getElementById('input-code').value == '1234','5678','9809'. That's not a valid conditional statement in JS. I assume you were trying to test if the value was equal to any of the strings, which can be done using || (A logical "or").
The code was never added to the end of the URL.
You never defined the url variable you were redirecting to.
Here's a commented version that should explain some ways to do this:
function btntest_onclick() {
// First, we assign the value to a variable, just to keep the code tidy
var value = document.getElementById('input-code').value
// Now we compare that variable against each valid option
// if any of these are true, we will progress
if (value === '1234' || value === '5678' || value === '9809') {
// Use a template literal (The ` quotes) to build the new URL
var url = `http://www.website.com?code=${value}`
// This could also be written as:
// var url = "http://www.website.com?code=" + value
// Navigate to your new URL (Replaced with an alert as a demonstration):
alert(url)
// window.location.href = url
} else {
// Otherwise, show the alert
alert('not found')
}
}
<center>
<span class="text-container">
<input type="text" name="accesscode" placeholder="ACCESS CODE" maxlength="10" size="25" id="input-code">
<p>ENTER</p>
</span>
</center>

google.script.run.withSuccessHandler does not work properly

I have a HTML code setup within my Google Apps Script project which basically validates the userid and password provided that pair exists in the gSheet or not and according to the verification I either show an alert or redirect to another web-page. When I deploy the code as a web-app, HTML renders just fine, submit button works and executes the gs code however it does not return the value back to the withSuccessHandler function of JS inside the script tag in the HTML code.
I have read a couple of very identical queries in the forum and followed exactly the same path, however I couldn't be able to succeed. For example like this post
It seems there's something that my eyes and mind cannot see - what is it?
Here is the HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div>
<form id="user_login">
<input type="text" name="userid" id="userid" placeholder="enter username"/>
<input type="text" name="pswrd" id="pswrd" placeholder="enter password"/>
<input type="submit" name="login" id="login" value="LOGIN"/>
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>
$('#login').click(function() {
console.log("Form submitted...");
google.script.run
.withSuccessHandler(function(response) {
console.log(response);
if (response == true) {
window.open("http://www.google.com", "_self");
} else {
alert("Some alert message here")
}
})
.checkName({
userid: $('#userid').val(),
pswrd: $('#pswrd').val()
});
});
</script>
</body>
</html>
And here is the gs functions
var html;
var verified;
function doGet() {
html = HtmlService.createTemplateFromFile('myformtest').evaluate().setTitle('Web App').setSandboxMode(HtmlService.SandboxMode.NATIVE);
return html
}
function checkName(form){
Logger.log("Form info > "+JSON.stringify(form));
Logger.log("Userid > "+form.userid);
Logger.log("Pwd > "+form.pswrd);
verified = false;
var ss = SpreadsheetApp.openById("1rard7vdpjmfrN81oi2ARhMlLGdNSxC7MqoIw2soj25E").getSheetByName("names");
var values = ss.getDataRange().getValues();
for(n=1;n<values.length;++n){
var username = values[n][0]; // 1 is the index of the column starting from 0
var password = values[n][1];
Logger.log("username: "+username+" | password: "+password);
if (username == form.userid && password == form.pswrd) {
Logger.log("User found in Row("+(n + 1)+")");
verified = true;
break;
}
}
Logger.log("User verified? "+verified);
return verified
}
I can log that the JS is fired correctly with passing the form parameters to the gs function correctly and the gs function executed correctly as well. However, it does not return the true/false value back to the handler function inside the JS.
I expect that when the checkName function is executed, to return the variable's value back to the JS that is to be processed within the withSuccessHandler function and resulting with either redirecting to a definite URL or displaying an alert message.

How can I process a form using Google Apps Script?

I am a total noob when it comes to GAS, but I want to pass a form to a local JS function (to validate the data), which then calls a Google function (to add it to a spreadsheet).
Problem is: I can't even get the values out of the form!
My code is like this at the moment:
index.html:
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<div>
<form id="register" action="javascsript:void(0)" onsubmit="validateForm(this)">
Email: <input type="text" name="email" placeholder="someone#example.com" /><br/>
<p id="emailtext"></p><br/>
Smartschool URL: <input type="text" name="url" /><br/>
<p id="urltext"></p><br/>
<input type="submit" value="Submit" />
</form>
</div>
<?!= include('Javascript'); ?>
Javascript.html:
<script>
function validateForm(form) {
// THIS IS NEVER POPPING UP
alert(form.email);
return false;
}
</script>
GoogleCode.gs:
function doGet(e) {
return HtmlService.createTemplateFromFile('Page').evaluate();
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
I added a console.log statement to the JavaScript, and looked at the log in my Google Chrome browsers console, and it shows that a form object is getting passed.
I added this line:
console.log('form: ' + form);
To your Javascript file:
<script>
function validateForm(form) {
console.log('form: ' + form);
// THIS IS NEVER POPPING UP
alert(form.email);
return false;
}
</script>
The browser console prints:
form: [domado object HTMLFormElement FORM]
So, the form object is getting passed. You can enumerate all the properties in the Form object to see what is in there, and available to retrieve.
for (var thePropurtees in form) {
console.log('thePropurtees: ' + thePropurtees);
};
You'll get a real long list of everything in the Form object, and you'll notice that email is not in the list. What is in the list is an elements property, that turns out to be another object inside the form object. There is an elements object inside of the form object.
If I enumerate the form elements:
for (var thePropurtees in form.elements) {
console.log('thePropurtees: ' + thePropurtees);
};
I get this:
thePropurtees: 0
thePropurtees: 1
thePropurtees: 2
thePropurtees: item
thePropurtees: email
thePropurtees: url
thePropurtees: namedItem
So, your email data must be in a sub object.
I was able to get the value out of the email input field with this:
console.log('the email value: ' + form.elements.email.value);
There are three levels of objects you need to access, before you can get at the values.
1) Form object
2) Elements object
3) Input object (email)
Your alert would need to be like this:
alert(form.elements.email.value);

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

How can I assign different actions for different submit buttons in same html form?

I am trying to assign different actions to same html form according to different submit buttons.
Can I do something like this ?
<FORM>
------
<INPUT type="submit" value="DoSomething" action="DoSomething.pl" method="POST">
<INPUT type="submit" value="DoSomethingElse" action="DoSomethingElse.pl" method="POST">
<FORM/>
Just in case someone else finds this post:
If you're using HTML5, this is now easier thanks to the formaction attribute. This attribute applies to input and button elements of type="submit" and forces the form to submit to the location specified in the formaction attribute of the clicked element.
Then only drawback of this attribute is that it's not supported by Internet Explorer 9 and lower, but this limitation can be easily overcome using a little JavaScript.
Example:
<form method="post" action="go_default">
<input type="submit" value="Go Left" formaction="go_left" />
<input type="submit" value="Go Right" formaction="go_right" />
</form>
For IE 9 and lower:
<script type="text/javascript">
$(function () {
var $submit = $('form [type="submit"][formaction]');
$submit.click(function() {
var $this = $(this),
action = $this.prop('formaction'),
$form = $this.closest('form');
$form.prop('action', action).submit();
});
});
</script>
No. A form has only one action (action being a property of the form, not the submit button).
The target of the action can do different things on the basis of the values in the form. So, you might want to start naming your submit buttons.
Learn HTML before you even think about writing and deploying a CGI script.
<form method="POST" action="/cgi-bin/script">
<input type="submit" name="action" value="DoSomething">
<input type="submit" name="action" value="DoSomethingElse">
</form>
Note also that choosing an action based on the value of the submit button is a losing strategy if you wish to internationalize the application because the value of a submit button is what the UA displays to humans.
Therefore, script should decide what to do on the basis of some other input element's value.
For example, CGI::Application looks at a run_mode parameter.
Alternatively, you can use different names for your submit buttons as Alec suggests. In that case, you need to check which submit button was pressed by going through the names of the parameters passed to your script which, IMHO, makes the dispatch slightly more cumbersome. It also means it is possible for someone to pass values for all submit buttons to your script (not via the user interface, but via curl or wget or similar programs.
For example, given the HTML
<form method="POST" action="/cgi-bin/script">
<input type="submit" name="submit_left" value="Go Left">
<input type="submit" name="submit_right" value="Go Right">
</form>
here is how your script may handle form submission:
#!/usr/bin/perl
use strict; use warnings;
use CGI::Simple;
my $cgi = CGI::Simple->new;
my %dispatch = (
left => \&handle_left,
right => \&handle_right,
);
my #actions = grep s/^action_(right|left)\z/$1/, $cgi->param;
my $handler = \&handle_invalid_action;
if ( #actions == 1) {
my ($action) = #actions;
if ( exists $dispatch{ $action } ) {
$handler = $dispatch{ $action };
}
}
else {
$handler = \&handle_too_many_actions;
}
$handler->($cgi);
sub handle_left { }
sub handle_right { }
sub handle_invalid_action { }
# because it may indicate someone trying to abuse your script
sub handle_too_many_actions { }
You can also use Ajax for this, and every button has assigned an ajax function that calls it's own php script and you don't even need to refresh the page or redirect, like in this example that i have tried:
HTML:
<input type="submit" value="Make other thing" onclick="ajax_post1();"/>
<input type="submit" value="Make something" onclick="ajax_post2();"/>
<div id="script1Response"></div>
<div id="script2Response"></div>
Javascript functions:
//the first function
function ajax_post1(){
var hr = new XMLHttpRequest();
//take the values from the html input elements you want to use
var v1=document.getElementbyId("element1").value;
var v2=document.getElementbyId("element2").value;
//the script that will process the data
var url="php_script1.php";
//the variable that will contain the information for the php script
var dataVar="var1="+v1+"&var2="+v2;
hr.open("POST", url, true);
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Access the onreadystatechange event for the XMLHttpRequest object
hr.onreadystatechange = function() {
if(hr.readyState == 4 && hr.status == 200) {
var script_response = hr.responseText;
document.getElementById("script1Response").innerHTML = script_response;
}
}
// Send the data to php_script1.php
hr.send(dataVar); // Actually execute the request
document.getElementById("script1Response").innerHTML = "processing...";
}
//the second function
function ajax_post2(){
var v1=document.getElementbyId("element1").value;
var v2=document.getElementbyId("element2").value;
var url="php_script2.php";
var dataVar="var1="+v1+"&var2="+v2;
var hr = new XMLHttpRequest();
hr.open("POST", url, true);
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
hr.onreadystatechange = function() {
if(hr.readyState == 4 && hr.status == 200) {
var script_response = hr.responseText;
document.getElementById("script2Response").innerHTML = script_response;
}
}
hr.send(dataVar);
document.getElementById("script2Response").innerHTML = "processing...";
}
The php files will have to contain some variables that will store the values sent by dataVar parameter like this:
$var1_=$_POST['var1']; //the var1 from the dataVar parameter
$var2_=$_POST['var2']; //the var2 from the dataVar parameter
The example I used can be found here:
https://www.youtube.com/watch?v=woNQ2MA_0XU.