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.
Related
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>
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();
}
Hi I ran into the bug where Chrome does not offer to remember userid & passwords.
I tried the solution in link below but I could not get it to work. Has anybody solved this for React?
http://code.google.com/p/chromium/issues/detail?id=43219
I implemented the solution in the above link but it did nothing when using react.
Many Thanks!
Sample Code as requested:
The original workaround is described in the link above - I have included here:
This is a Chrome bug and here is the recommended workaround:
http://code.google.com/p/chromium/issues/detail?id=43219
As a followup to comment #5, here is a fuller explanation of a workaround that I have tested.
On your login page, include a form with two input elements. Set the name and id of one to "userid" and the other to "password". E.g.,
<input value type="text" id="userid" name="userid" autocomplete="on">
<input value type="password" id="password" name="password" autocomplete="on">
Give the form a POST action to a blank html page served from the same domain, say blank.html. Set the onsubmit handler to a JavaScript function:
<form name="login" id="login" method="POST" action="blank.html" onsubmit="return do_login();">
Above the form, include a hidden iframe element, like so:
<iframe src="login.html" style="width:0px;height:0px;border:0px;" id="hidden" name="hidden"> </iframe>
The login.html file must also be served from the same domain, and it should contain an identical form (same names and ids, same action and method) as the one on the real login form.
In your JavaScript do_login handler, perform the authentication however you like. To get Chrome to prompt the user to store the password, have the JavaScript code copy the values from the real form to the hidden form, then submit the hidden form, like so:
var userid = document.getElementById("userid").value;
var password = document.getElementById("password").value;
var iframe = document.getElementById("hidden");
var iframedoc = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
var fake_login_form = iframedoc.getElementById("login");
var fake_userid = iframedoc.getElementById("userid");
fake_userid.value = userid;
fake_password.value = password;
fake_login_form.submit();
Tested with Chrome 10.0.648.204.
Here is what I did for React:
I created a 'blank form' and configured an nginx rule:
Included a dummy login form which is rendered and hidden:
Note: I am using react-frame-component
/**
* #jsx React.DOM
*/
'use strict';
var React = require('react/addons');
var Frame = require('react-frame-component');
var Input = require('react-bootstrap/Input');
var DummyLoginForm = React.createClass({
_onChange: function(){
console.log('DummyLoginForm onChange called')
},
_onSubmit: function() {
console.log('DummyLoginForm onsubmit called');
var email = this.refs.userid.getValue();
var password = this.refs.password.getValue();
console.log(email);
console.log(password);
},
render: function () {
var content = (
<div>
<Frame id="hidden" width="0" height="0" name="hidden">
<form role="form" name="dummylogin" id="dummylogin" ref="dummylogin" method="POST" action="/login/index.html" onsubmit="return _onSubmit();">
<input value type="text" id="userid" name="userid" ref="userid" autoComplete="on" onChange={this._onChange}/>
<input value type="password" id="password" name="password" autoComplete="on" onChange={this._onChange}/>
</form>
</Frame>
</div>
);
return content;
}
/*jshint ignore:end */
});
module.exports = DummyLoginForm;
In the real login modal I do this:
I am using react-bootstrap modal
onSubmit: function(e) {
if(e && typeof e !== 'undefined') {
e.stopPropagation();
e.preventDefault();
}
var email = this.refs.email.getValue();
var password = this.refs.password.getValue();
MyUtils.login(email, password);
// workaround to force chrome to prompt to save password details
if(BrowserUtils.isChrome()) {
console.log("Chrome");
var iframe = document.getElementById("hidden");
var iframedoc = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
var fake_login_form = iframedoc.getElementById("dummylogin");
var fake_userid = iframedoc.getElementById("userid");
var fake_password = iframedoc.getElementById("password");
fake_userid.value = email;
fake_password.value = password;
fake_login_form.submit();
}
},
The _onSubmit: function() in DummyLoginForm does not get triggered
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);
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