Hopefully I've included enough of the code w/o having to post it all...
I have a main function that calls displayDropdown()- which calls an HTMLService and displays a modal with a dropdown and a text box:
.
This is the (condensed) javascript code that stores the data:
<html>
<input type="submit" value="Submit" class="action" onclick="sendData()" />
</html>
<script>
function sendData() {
var values = {};
values.textJob = document.getElementById("input").value;
values.selectedJob = document.getElementById("dropJob").value;
google.script.run.withSuccessHandler(closeIt).grabData(values);
};
function closeIt(){
google.script.host.close()
};
</script>
Then the grabData() function in my .gs file:
function grabData(values) {
if(values.textJob=="")
//return values.selectedJob;
Logger.log(values.selectedJob);
else
//return values.textJob;
Logger.log(values.textJob);
}
If I keep the returns commented out and try to log the data, I get the expected data logged. But if I reverse that, and return it instead, go back up to the main function just after displayDropdown() was called, and set a variable to equal the grabData function:
displayDropdown();
var stuff = grabData();
Logger.log(stuff);
I get an error that says:
Why can't I access the data?
This is what I usually do to send data from HTML form to GS:
HTML
<form method="POST" action="#" id="formID">
<button class="btn" type="submit">Send</button>
</form>
JS
document.querySelector("#formID").addEventListener("submit", function(e) {
var test = google.script.run.withSuccessHandler('client side function').processForm(this);
});
I usually pass 'this' as an argument and I process the information on the GS.
EDIT:
GS
function processForm(values){
Logger.log(values);
Logger.log(typeof values);
}
Screenshoots:
1- Web app
2- Server logs (function processForm)
Related
I am creating an editor add-on for google sheets and I'm currently stumped on why my user property (MY_WEBHOOK) is being changed every time I try to save it with setProperty to this code:
function(){var L=h(fb(arguments));if(b&32)Vd(function(){a.apply(g||this,L)});else return m(a.apply(g||this,L))}
Here is the code I am using:
code.gs:
function saveSettings(url) {
PropertiesService.getUserProperties().setProperty('MY_WEBHOOK', url);
SpreadsheetApp.getUi().alert("Saved")
}
function getSettings() {
return PropertiesService.getUserProperties().getProperty('MY_WEBHOOK');
}
In my html file:
<body onload="get()">
<form>
<label>What is the URL?</label>
<input id="webhook-url" type="url" autofocus required placeholder="Webhook Here">
<br><br>
<input type="button" value="Save" onclick="save()">
<script>
function save() {
var url = document.getElementById('webhook-url').value
google.script.run.saveSettings(url)
alert(url)
alert(google.script.run.getSettings)
google.script.host.close()
}
function get() {
var settings = google.script.run.getSettings
document.getElementById('webhook-url').value = settings;
}
</script>
</form>
</body>
Modification points:
I think that there are 2 modification points in your script.
About google.script.run.getSettings, in this case, the function of getSettings is not run. Please add () like google.script.run.getSettings(). By this, the function is run.
I think that this is the reason of your issue.
About alert(google.script.run.getSettings) and var settings = google.script.run.getSettings, google.script.run returns no value. So in this case, withSuccessHandler is used.
google.script.run is run with the asynchronous process.
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify your Javascript as follows.
function save() {
var url = document.getElementById('webhook-url').value
google.script.run.withSuccessHandler(() => {
google.script.run.withSuccessHandler(e => {
alert(e);
google.script.host.close();
}).getSettings();
}).saveSettings(url);
}
function get() {
google.script.run.withSuccessHandler(settings => {
document.getElementById('webhook-url').value = settings;
}).getSettings();
}
When above script is used, at first, the value is retrieved from getSettings by get(), and when the value is inputted and click button, the value is put by saveSettings() and the current value is retrieved by getSettings() and alert() is run, and then, google.script.host.close() is run.
Note:
This is a simple modification. So please modify it for your actual situation.
Reference:
Class google.script.run
I'm new to security concepts. I have a program that is written in javascript and therefore can only be run in the browser. I was wondering is there any way to inject javascript code in this program?
My program looks like this:
index.html:
<form method="get" action="file.html">
Enter your name: <input name="name" type="text"> <input type="submit" value="enter">
</form>
file.html:
<head>
<script src="js/main.js"></script>
</head>
<body>
<div id="contents">
</div>
</body>
js/main.js:
function get_parameters() {
// 1. get the string of the get parameters after question mark
var parameters = window.location.search.substr(1);
// 2. make an array of parameters
parameters = parameters.split('&')
// 3. retrieve parameters
var res = {}
parameters.forEach(function (item) {
var tmp = item.split('=');
res[tmp[0]] = decodeURIComponent(tmp[1]);
})
// 4. return parameters
return res;
}
window.onload = function () {
var parameters = get_parameters();
if ('name' in parameters) {
document.getElementById('contents').innerHTML = '<div> Hello ' + parameters['name'] + '</div>';
}
};
Scripts can be inserted via the url. If you visit file.html?name=%3Cimg%20onerror%3Dalert(1)%20src%3D/%3E then you should see an alert.
The use of the innerHTML property is setting HTML containing user controlled content. If instead you use innerText then it wouldn't be possible. For example:
document.getElementById('contents').innerText = 'Hello ' + parameters['name'];
I'm new to asynchronous calls and I think this is the problem. However, i'm not too sure how to fix it since Google Apps Script does not support promises and I also don't know how to use them. I've heard that if HTML Service is used in GAS, then promises are possible and this is what I'm using. However, I'm at a loss on how to implement this. Here is what I have so far. The main PROBLEM IS THAT I need the data to show in the second Logger.log on the server-side (code.gs) below. The data gets to the function in the first logger.log (code.gs), but then the object is empty (not null), when displaying the user cache in the second logger.log (code.gs). Any keys/data can be used and problem can be replicated, so it has something to do with asynchronous calls, but how do I fix it in the GUI_JS code?
Server-side (code.gs):
// global variable to save into the cache
var userCache = CacheService.getUserCache();
// SAVE OPERATION - saves the checkboxes into the user cache
function processSavedValues(checkboxesObj){
Logger.log(checkboxesObj); // this line shows in Logger
userCache.putAll(checkboxesObj, 20);
var getCache = userCache.getAll(['key1','key2']);
Logger.log(getCache); // this line doesn't show in Logger
}
// Loads the HTML Service of Apps Script
function doGet(request) {
return HtmlService.createTemplateFromFile('index').evaluate();
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
Client-side (index.html):
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form>
<fieldset class="columnList">
<div>
<input type="checkbox" id="key1" name="fieldset[]" value="value1">
<label class="checkmark" for="key1">test1</label>
</div>
<div>
<input type="checkbox" id="key2" name="fieldset[]" value="value2">
<label class="checkmark" for="key2">test2</label>
</div>
</fieldset>
</form>
<button onclick="saveAllCheckboxValues()">test</button>
<?!= include('GUI_JS'); ?>
</body>
</html>
Client-side using HTML Service (GUI_JS.html):
<script>
// Saves all checkbox values into the cache
function saveAllCheckboxValues(){
// Select all checkboxes in the document
var allCheckboxes = document.querySelectorAll("input[type=checkbox]");
// Create a Key/Value pairs with the IDs and values of each checkbox
var checkboxesObj = {};
for (var i = 0; i < allCheckboxes.length; i++) {
checkboxesObj[allCheckboxes[i].id] = allCheckboxes[i].checked;
}
// sends the checkbox values server-side into the cache
google.script.run.withSuccessHandler(checkboxSaved).processSavedValues(checkboxesObj);
// displays successfully saved
function checkboxSaved(){
alert("Great Success!");
}
}
</script>
The result of Logger.log:
[19-03-14 18:28:38:913 PDT] {key1=true, key2=true}
[19-03-14 18:28:38:959 PDT] {}
I think that the reason of your issue is the boolean values in the object for putting to CacheService. At CacheService, the string value is used for putting. So how about this modification? Please think of this as just one of several answers. In my modification, the function of processSavedValues() was modified.
Modified script:
function processSavedValues(checkboxesObj){
Logger.log(checkboxesObj);
userCache.put("sampleKey", JSON.stringify(checkboxesObj), 20); // Modified
var getCache = userCache.get("sampleKey"); // Modified
getCache = JSON.parse(getCache); // Added
Logger.log(getCache);
}
References:
put(key, value)
get(key)
If this didn't work and this was not the result you want, I apologize.
I'm trying to return a 2D array from a google spreadsheet using this:
function getData(){
//this function reads data from the sheet and retuns a 2D array
var spreadsheetID = //spreadsheet ID;
var range = 'SchoolData!A2:B';
var data =
SpreadsheetApp.openById(spreadsheetID).getRange(range).getValues();
return data;
}
The returned data should be processed in the compareID() function upon submission of my HTML form.
<input type="submit" value="Submit" onclick= "authorise()">
function authorise(){
alert("inside authorise");
google.script.run
.withFailureHandler(errMsg)
.withSuccessHandler(compareID)
.getData();
}
function compareID(data){
alert("inside compare");
//process the data
}
function errMsg(){
alert("no data was returned ");
}
My code seems to always go to errMsg() and never enters the compareID() function. I even tried to use JSON.stringify(data) in the Google script file and return a String then JSON.parse(data) in the HTML script but still did not get the required results.
What should i do to get my code to enter the compareID() function?
Using your code as shown above i get "inside compare". And data is correct values. Is errMsg called anywhere else in your html?
function test() {
try {
var htmlTemplate = HtmlService.createTemplateFromFile('HTML_Test');
var htmlOutput = htmlTemplate.evaluate();
SpreadsheetApp.getUi().showModalDialog(htmlOutput, "Sample");
}
catch(err) {
Logger.log(err);
}
}
function getData(){
//this function reads data from the sheet and retuns a 2D array
var spreadsheetID = SpreadsheetApp.getActiveSpreadsheet().getId();
var range = 'Sheet2!A2:B';
var data = SpreadsheetApp.openById(spreadsheetID).getRange(range).getValues();
return data;
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div>
<input type="submit" value="Submit" onclick= "authorise()">
</div>
<script>
function authorise(){
alert("inside authorise");
google.script.run
.withFailureHandler(errMsg)
.withSuccessHandler(compareID)
.getData();
}
function compareID(data){
alert("inside compare");
//process the data
}
function errMsg(){
alert("no data was returned ");
}
</script>
</body>
</html>
It looks like you are trying to use the Google Sheets API directly, but when using Apps Script you have access to the much more robust and convenient SpreadsheetApp service.
var data = SpreadsheetApp.openById(spreadsheetID).getRange(range).getValues();
see: https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
The form will automatically send when page load, it will send to the iframe. But I want get the iframe content in the main page, so I create a function to test, parent.function. But I don't know where I should put this function and the parent.function. Firebug TypeErroe: parent.incomingValue is not a function. Help, appreciate.
$('#frame_form').submit();
parent.incomingValue("Hello world");
function incomingValue(val) {
alert(val)
}
<form target="iframe">
<input type="hidden" value="send"/>
</form>
<iframe name="iframe"> </iframe>
You should declare 'parent' as an object and set 'incomingValue' as a property like this:
var parent = {};
parent.incomingValue = function(val) {...}