Saving checkboxes asynchronously in Google Apps Script - javascript

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.

Related

Why is google apps script changing my user property to some random code every time I save it?

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

Passing data from dropdown => to javascript function (Google App script)

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)

Save Button-Click Count in localStorage in javascript

I made a button click counter for a website using some JavaScript.
The counter works well, but now I'm stuck in making the saving of the count. You know, if I click the button 3 times, the text says 3 Times. But I want to save that value so if the user refreshes the page, it should display 3 Times again.
I knew of using localStorage, I followed a simple tutorial and applied it to my code, but it does not seem to be working. When I run the page in Microsoft Edge and see the Debug page (F12), the console throws an error that says: Unable to get property 'getItem' of undefined or null reference. I searched in other posts but no one of these could solve my problem. It seems to be stuck when retrieving the value in localStorage.
This is my code:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Increment count when button is clicked</title>
</head>
<body>
<input type="button" value="Registrar" id="countButton" />
<input id="ocityField" type="text" value="" placeholder="Ciudad de Origen"/>
<input id="cityField" type="text" value="" placeholder="Ciudad de participación"/>
<input id="name" type="text" value="" placeholder="Nombre"/>
<p>Personas Registradas: <span id="displayCount">0</span></p>
<script type="text/javascript">
var count = 0;
var button = document.getElementById("countButton");
var display = document.getElementById("displayCount");
var textbox = document.getElementById("ocityField");
var textboxa = document.getElementById("cityField");
var textboxb = document.getElementById("name");
if(window.localStorage.getItem('count')){
var savedcount = window.localStorage.getItem('count');
count = window.localStorage.getItem('count');
}else{
count = 0;
}
display.innerHTML = count;
button.onclick = function(){
var mystring = textbox.value;
var mystring2 = textboxa.value;
var mystring3 = textboxb.value;
if(!mystring.match(/\S/) || !mystring2.match(/\S/) || !mystring3.match(/\S/)) {
alert ('Empty value is not allowed');
return false;
} else {
count++;
window.localStorage.setItem('count', count);
display.innerHTML = count;
textbox.value = "";
textboxa.value = "";
textboxb.value = "";
return true;
}
}
</script>
</body>
</html>
I tried using window.localStorage and just localStorage but no one did work.
May be that you use the IE browser does not support localStorage,The code can run in Chrome49.
Can I Use localStorage, here you can check what browser supports localStorage with version numbers.
Alternate way to store data on client side is cookies if localStorage doesn't supported by browser.
You can also use third party plugins like Modernizer, to check whether browser supports or not.
Modernizr.localstorage if it evaluate to true the browser supports localStorage.
Following example demonstrates localStorage and cookies depending on browser compatibility. uses Modernizer and jQuery
codepen

Google Apps Script server call to populate select list never runs

I'm creating a sidebar in a Google sheet. I have no problem getting it to appear with select lists. I'm trying to populate the select lists based on a range in one of the sheets. I've tried numerous suggestions from stackoverflow and I've read Google's documentation for the HTML Service many times. I still seem to be missing something. From my testing, it appears that the google.script.run call to the server is never executed. Here is my code.gs file:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Schedule')
.addItem('New schedule', 'newSchedule')
.addItem('Edit start date', 'newDate')
.addSeparator()
.addItem('Insert names', 'openInsert')
.addToUi();
}
function openInsert() {
var html = HtmlService.createHtmlOutputFromFile('Insert')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('Insert Names')
.setWidth(300);
SpreadsheetApp.getUi()
.showSidebar(html);
}
function getNames() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Publishers");
var names = sheet.getRange(2, 1, sheet.getLastRow(),1).getValues();
return names;
}
Here is my Insert.html file:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<script>
function returnedNames(names) {
var selectList = new Array("publisher1","publisher2","publisher3");
for (sel in selectList) {
var dropDown = document.getElementById(sel);
for (i = 0; i < names.length; i++) {
var opt = document.createElement("option");
opt.text = names[i][0];
opt.values = names[i][0];
dropDown.options.add(opt);
}
}
}
google.script.run.withSuccessHandler(returnedNames).getNames();
</script>
</head>
<body>
<div class="block form-group">
<label for="publisher1">First publisher</label>
<select id="publisher1"> </select>
<label for="publisher2">Second publisher</label>
<select id="publisher2"> </select>
<label for="publisher3">Third publisher (optional)</label>
<select id="publisher3"> </select>
</div>
</body>
</html>
I believe your server function is being called before your document is actually ready to be worked on. I'd imagine that if you checked the browser console window with developer tools, you'd see an error pop up.
Since you essentially want these populated onload, add a load event to the body tag. Have that event call your server function. If this doesn't work use a combination of Logger.log on the server side and console.log on the client side through out your sequence of code and see what you get and what path your code follows.

Bug displaying a div element when a user clicks on a button

I'm writing a webpage and I need to display a div with some content when a user clicks on a button.
I've written the code below and I don't understand why it doesn't work.
Does someone know why ?
My code :
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso 8859-1" />
<script type="text/javascript">
function traverse(){
output.innerHTML+='Test'; // Nothing happens !
}
function check() {
var keywords = document.getElementById('text').value.split(" ");
for (var i=0; i < keywords.length; ++i) {
traverse_tree()
}
}
</script>
</head>
<body onload ="init()">
<input id="text" type="text" size="60" value="Type your keywords here" />
<input type="button" value="Display the text 'Test'" onclick="check();" />
<div id="output">
</div>
</body>
</html>
thanks,
Bruno
Perhaps because the function is called traverse() and you're calling traverse_tree()?
Also, in your method traverse, you should get the element using document.getElementById('output'), instead of using a (undefined) variable output:
i.e:
function traverse(){
document.getElementById('output').innerHTML+='Test';
}
You could also speed this up by caching the node (to avoid calling getElementById each time the button is clicked):
// Create a closure by wrapping the cached node in a self-executing
// function to avoid polluting the global namespace
var traverse = (function (nodeId) {
// Cache the node to be updated here
var node = document.getElementById(nodeId);
// This is the function that "traverse()" will call. Return this function,
// which will assign it to the variable traverse.
return function () {
node.innerHTML += 'test';
};
// Execute the function with the id of the node to cache, i.e. output
}('output'));

Categories