How to define global variable in Google Apps Script - google-apps-script

I see most examples from Google is they use only functions in a single giant script.
e.g. https://developers.google.com/apps-script/quickstart/macros
But in our style, we usually write all functions under a single namespace, such as
MyCompany = (MyCompany || {});
MyCompany.init = function () {
Logger.log('init');
};
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menus = [{
name: "Init",
functionName: MyCompany.init
}];
spreadsheet.addMenu("Test", menus);
};
However, when I run the code above, it return
"MyCompany is not defined."
How to solve?

You might be better off using the Properties Service as you can use these as a kind of persistent global variable.
click 'file > project properties > project properties' to set a key value, or you can use
PropertiesService.getScriptProperties().setProperty('mykey', 'myvalue');
The data can be retrieved with
var myvalue = PropertiesService.getScriptProperties().getProperty('mykey');

In GAS global variables are not what they are in other languages. They are not constants nor variables available in all routines.
I thought I could use global variables for consistency amongst functions and efficiency as well. But I was wrong as pointed out by some people here at SO.
Global variable will be evaluated at each execution of a script, so not just once every time you run your application.
Global variables CAN be changed in a script (so they are not constants that cannot be changed by accident), but will be reinitialized when another script will be invoked.
There is also a speed penalty on using global variables. If within a function you use the same global variable two or more times, it will be faster to assign a local variable and use that instead.
If you want to preserve variables between all functions in your application, it might be using a cacheService will be best.
I found out that looping through all files and folders on a drive takes a LOT of time. But you can store info about files and folders within cache (or even properties) and speed up at least 100 times.
The only way I use global variables now is for some prefixes and for naming widgets.

I'm using a workaround by returning a function with an object of my global variables:
function globalVariables(){
var variables = {
sheetName: 'Sheet1',
variable1: 1,
variable2: 2
};
return variables;
}
function functionThatUsesVariable (){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(globalVariables().sheetName);
}

Global variables certainly do exist in GAS, but you must understand the client/server relationship of the environment in order to use them correctly - please see this question:
Global variables in Google Script (spreadsheet)
However this is not the problem with your code; the documentation indicates that the function to be executed by the menu must be supplied to the method as a string, right now you are supplying the output of the function:
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#addMenu%28String,Object%29
function MainMenu_Init() {
Logger.log('init');
};
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menus = [{
name: "Init",
functionName: "MainMenu_Init"
}];
spreadsheet.addMenu("Test", menus);
};

I needed something similar like the question, you can store and fetch from the cache https://developers.google.com/apps-script/reference/cache/cache
Example:
// call cache service
var cache = CacheService.getScriptCache();
// get an item from the cache
var cached = cache.get("somekey");
// if exists in the cache use it
if (cached != null) {
// use it whatever you like.
}else{
// calculate/assign your data to cache
cache.put("somekey","somevalueorobject");
// you can even put cache data on TTL (time to live) in seconds.
cache.put("somekey","somevalueorobject",60);

I use this: if you declare var x = 0; before the functions declarations, the variable works for all the code files, but the variable will be declare every time that you edit a cell in the spreadsheet

For constants I am using function arrow expressions.
The footprint is similar to a variable declaration. Just add the () => when declaring, and () when calling the (function) variable.
var currentSheet = () => SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var maxAttempts = () => 10;
function myFunction(){
var sheetName = currentSheet().getName();
for (var i=0; i< maxAttempts(); i++){
trySomething(i);
}
}

var userProperties = PropertiesService.getUserProperties();
function globalSetting(){
//creating an array
userProperties.setProperty('gemployeeName',"Rajendra Barge");
userProperties.setProperty('gemployeeMobile',"9822082320");
userProperties.setProperty('gemployeeEmail'," rajbarge#hotmail.com");
userProperties.setProperty('gemployeeLastlogin',"03/10/2020");
}
var userProperties = PropertiesService.getUserProperties();
function showUserForm(){
var templete = HtmlService.createTemplateFromFile("userForm");
var html = templete.evaluate();
html.setTitle("Customer Data");
SpreadsheetApp.getUi().showSidebar(html);
}
function appendData(data){
globalSetting();
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
ws.appendRow([data.date,
data.name,
data.Kindlyattention,
data.senderName,
data.customereMail,
userProperties.getProperty('gemployeeName'),
,
,
data.paymentTerms,
,
userProperties.getProperty('gemployeeMobile'),
userProperties.getProperty('gemployeeEmail'),
Utilities.formatDate(new Date(), "GMT+05:30", "dd-MM-yyyy HH:mm:ss")
]);
}
function errorMessage(){
Browser.msgBox("! All fields are mandetory");
}

Related

How to get files from google drive by filtering based on 'Unknown' criteria with the apps script?

I want to retrieve a list of files from google drive using GAS.
So I want the list of files based on the owner who is empty.
For files that we create on the share drive, the owner doesn't exist.
If we try to force the owner name, it will get an error.
For example like this:
file.getOwner().getName();
it cannot be retrieved from the file list.
I have tried adding a filter based on an empty owner, I would like to say Unknown.
function getfilesUnknown () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var st = ss.getActiveSheet ();
var files = DriveApp.getFiles ();
var counter = 0;
while (files.hasNext () && counter <21) {
var file = files.next ();
var data = [
file.getName (),
file.getUrl (),
file.getOwner ()? file.getOwner (). getName (): 'Unknown'
];
var filter = data.filter (v => v [2] === 'Unknown');
counter ++;
st.appendRow (filter);
}
}
But the list of files won't retrieve.
It seems that the filter in while is not recommended.
Or maybe, folks here have another solution.
I am not sure if this is the best approach.
But since you are getting an error here file.getOwner().getName() when the owner does not exist, then try to use try...catch to catch the error.
Namely, if there is an error pass Unknown, otherwise pass the name of the file owner.
function getfilesUnknown() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var st = ss.getActiveSheet();
var files = DriveApp.getFiles();
var counter = 0;
var data = [];
while (files.hasNext() && counter<21) {
var file = files.next();
try{
data.push([file.getOwner().getName()]);
}
catch(e){
data.push(['Unknown']);
}
counter++;
}
st.getRange(st.getLastRow()+1,1,data.length,1).setValues(data);
}
Please note that your code is not very well optimized. You add information in the data array but then you filter on a particular value only. Therefore there is no need of using the other items as they will slow down your code.
Another note is that you are using appendRow iteratively which is not recommended. Instead use setValues outside of the for loop once.

Code shortening for data entry form Google App script

I'm new to coding, and I know I am going a long way about this and making my script run slow, but I can't figure out how to shorten and optimise it (now I have tried to map the second bit of code using Marios comment)
I have made a data entry form on Google Sheets for athletes I coach to use as a training diary. After recording training data in a session, they hit the save button and this script transfers it to a different spreadsheet with all of their training data ever in.
Below is a section of code I have attempted to shorten with Marios comment:
function submitSession1() {
workloadSubmit();
myValue();
}
function workloadSubmit(){
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var inputS = inputSS.getSheetByName("Session 1");
var outputSS = SpreadsheetApp.openByUrl()
var workloadS = outputSS.getSheetByName();
var dtCurrentTime = new Date();
//Input Values for Workload data
var workloads = [[inputS.getRange("M1").getValue(),
inputS.getRange("N1").getValue(),
inputS.getRange("O1").getValue(),
inputS.getRange("P1").getValue(),
inputS.getRange("AK3").getValue(),
inputS.getRange("AK5").getValue(),
inputS.getRange("AL3").getValue(),
inputS.getRange("AL5").getValue(),
inputS.getRange("BC3").getValue(),
inputS.getRange("BC5").getValue(),
inputS.getRange("BD3").getValue(),
inputS.getRange("BD5").getValue(),
inputS.getRange("AM3").getValue(),
inputS.getRange("AM5").getValue(),
inputS.getRange("AN3").getValue(),
inputS.getRange("AN5").getValue(),
inputS.getRange("AO3").getValue(),
inputS.getRange("AO5").getValue(),
inputS.getRange("AP3").getValue(),
inputS.getRange("AP5").getValue(),
inputS.getRange("AQ3").getValue(),
inputS.getRange("AQ5").getValue(),
inputS.getRange("AR3").getValue(),
inputS.getRange("AR5").getValue(),
inputS.getRange("AS3").getValue(),
inputS.getRange("AS5").getValue(),
inputS.getRange("AT3").getValue(),
inputS.getRange("AT5").getValue(),
inputS.getRange("AU3").getValue(),
inputS.getRange("AU5").getValue(),
inputS.getRange("AV3").getValue(),
inputS.getRange("AV5").getValue(),
inputS.getRange("AW3").getValue(),
inputS.getRange("AW5").getValue(),
inputS.getRange("AX3").getValue(),
inputS.getRange("AX5").getValue(),
inputS.getRange("AY3").getValue(),
inputS.getRange("AY5").getValue(),
inputS.getRange("AZ3").getValue(),
inputS.getRange("AZ5").getValue(),
inputS.getRange("BA3").getValue(),
inputS.getRange("BA5").getValue(),
inputS.getRange("BB3").getValue(),
inputS.getRange("BB5").getValue(),
dtCurrentTime]];
workloadS.getRange(workloadS.getLastRow()+1, 1, 1,
45).setValues(workloads);
}
// Drills Data Submit
function myValue(col) {
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var inputS = inputSS.getSheetByName("Session 1");
var outputSS = SpreadsheetApp.openByUrl()
var drillsS = outputSS.getSheetByName("Drills Data");
var dtCurrentTime = new Date();
return inputS.getRange(col).getValue();
}
var colns = ["M1", "N1", "O1", "P1", "A14","B14","D14","F14","G14","H14","J14","K14","L14","M14","N14","O14","P14","Q14","R14","S14","T14","U14"];
var drillsData = colns.map(myValue)
drillsData.push(dtCurrentTime)
I am now getting the error code:
Exception: Argument cannot be null: a1Notation (line 71, file "Code")Dismiss
Any help is much appreciated
You can calculate drillsData using maps:
function myValue(col) {
return inputS.getRange(col).getValue();
}
var colns= ["M1", "N1", "O1", "P1", "A14","B14","D14","F14","G14","H14","J14",
"K14","L14","M14","N14","O14","P14","Q14","R14","S14","T14","U14"];
var drillsData = colns.map(myValue)
drillsData.push(dtCurrentTime)
*Don't forget to call drillsData as [drillsData].
Unfortunately, the columns you want to retrieve are not sequential, therefore selecting the full range is not an option.
Or you can create custom functions to make your code look cleaner:
function importSheets(sheetN) {
return outputSS.getSheetByName(sheetN);
}
var workloadS = importSheets("W.L + Full Routine Data")
For the latter you can again create maps using the same logic described for one.
As a result, you can have a collection of sheets objects as elements in an array and call by using their index.

What alternative to ScriptDB I could use to store a big array of arrays? (without using external DB)

I was a user of the deprecated ScriptDB. The use I made of ScriptDB was fairly simple: to store a certain amount of information contained on a panel options, this way:
var db = ScriptDb.getMyDb();
function showList(folderID) {
var folder = DocsList.getFolderById(folderID);
var files = folder.getFiles();
var arrayList = [];
for (var file in files) {
file = files[file];
var thesesName = file.getName();
var thesesId = file.getId();
var thesesDoc = DocumentApp.openById(thesesId);
for (var child = 0; child < thesesDoc.getNumChildren(); child++){
var thesesFirstParagraph = thesesDoc.getChild(child);
var thesesType = thesesFirstParagraph.getText();
if (thesesType != ''){
var newArray = [thesesName, thesesType, thesesId];
arrayList.push(newArray);
break;
}
}
}
arrayList.sort();
var result = db.query({arrayName: 'savedArray'});
if (result.hasNext()) {
var savedArray = result.next();
savedArray.arrayValue = arrayList;
db.save(savedArray);
}
else {
var record = db.save({arrayName: "savedArray", arrayValue:arrayList});
}
var mydoc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setWidth(550).setHeight(450);
var panel = app.createVerticalPanel()
.setId('panel');
var label = app.createLabel("Choose the options").setStyleAttribute("fontSize", 18);
app.add(label);
panel.add(app.createHidden('checkbox_total', arrayList.length));
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 400);
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
}
function include(arr, obj) {
for(var i=0; i<arr.length; i++) {
if (arr[i] == obj) // if we find a match, return true
return true; }
return false; // if we got here, there was no match, so return false
}
function submit(e){
var scriptDbObject = db.query({arrayName: "savedArray"});
var result = scriptDbObject.next();
var arrayList = result.arrayValue;
db.remove(result);
// continues...
}
I thought I could simply replace the ScriptDB by userProperties (using JSON to turn the array into string). However, an error warns me that my piece of information is too large to be stored in userProperties.
I did not want to use external databases (parse or MongoDB), because I think it isn't necessary for my (simple) purpose.
So, what solution I could use as a replacement to ScriptDB?
You could store a string using the HtmlOutput Class.
var output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');
output.append('<p>Hello again, world.</p>');
Logger.log(output.getContent());
Google Documentation - HtmlOutput
There are methods to append, clear and get the content out of the HtmlOutput object.
OR
Maybe create a Blob:
Google Documentation - Utilities Class - newBlob Method
Then you can get the data out of the blob as a string.
getDataAsString
Then if you need to you can convert the string to an object if it's in the right JSON format.
Firstly, if you're hitting the limits on the Properties service, I would recommend you look at an alternative external store, as you're manipulating a large amount of data, and any workaround given here is possibly going to be slower and less efficient then simply using a dedicated service.
Alternatively of course, you could look at making your data come under the limits for the properties service by splitting it up and using multiple properties etc.
One other alternative would be to use a Google Doc or Sheet to store the string. When you're required to pull the data again, you can simply access the sheet and get the string, but this might be slow depending on the size of the string. At a glance it looks like you're just pulling Data on the folders in your drive, so you could consider writing it to a sheet, which would allow you to even display the information in a user friendly way. Given your use of arrays already, you can write them to a sheet easily using .setValues() if you convert them to a 2D array.
Bruce McPherson has done a lot of work on abstracting databases. Take a look at his cDbAbstraction library then you could easily chop and change which DB you use and compare performance. Maybe even create a cDbAbstraction library to use HTMLOutput (I like that idea Sandy, Bruce does some funky stuff with parallel processes via HTMLService)

Iterators built with different continuation tokens are producing the same results in Google Apps

I am programming a Google Apps script within a spreadsheet. My use case includes iterating over a large set of folders that are children of a given one. The problem is that the processing takes longer than the maximum that Google allows (6 minutes), so I had to program my script to be able to resume later. I am creating a trigger to resume the task, but that is not part of my problem (at least, not the more important one at this moment).
My code looks like this (reduced to the minimum to illustrate my problem):
function launchProcess() {
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty(SOURCE_PARENT_FOLDER_KEY, SOURCE_PARENT_FOLDER_ID);
scriptProperties.deleteProperty(CONTINUATION_TOKEN_KEY);
continueProcess();
}
function continueProcess() {
try {
var startTime = (new Date()).getTime();
var scriptProperties = PropertiesService.getScriptProperties();
var srcParentFolderId = scriptProperties.getProperty(SOURCE_PARENT_FOLDER_KEY);
var continuationToken = scriptProperties.getProperty(CONTINUATION_TOKEN_KEY);
var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFolders() : DriveApp.continueFolderIterator(continuationToken);
var timeLimitIsNear = false;
var currTime;
while (iterator.hasNext() && !timeLimitIsNear) {
var folder = iterator.next();
processFolder_(folder);
currTime = (new Date()).getTime();
timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);
}
if (!iterator.hasNext()) {
scriptProperties.deleteProperty(CONTINUATION_TOKEN_KEY);
} else {
var contToken = iterator.getContinuationToken();
scriptProperties.setProperty(CONTINUATION_TOKEN_KEY, contToken);
}
} catch (e) {
//sends a mail with the error
}
}
When launchProcess is invoked, it only prepares the program for the other method, continueProcess, that iterates over the set of folders. The iterator is obtained by using the continuation token, when it is present (it will not be there in the first invocation). When the time limit is near, continueProcess obtains the continuation token, saves it in a property and waits for the next invocation.
The problem I have is that the iterator is always returning the same set of folders although it has been built from different tokens (I have printed them, so I know they are different).
Any idea about what am I doing wrong?
Thank you in advance.
It appears that your loop was not built correctly. (edit : actually, probably also another issue about how we break the while loop, see my thoughts about that in comments)
Note also that there is no special reason to use a try/catch in this context since I see no reason that the hasNext() method would return an error (but if you think so you can always add it)
here is an example that works, I added the trigger creation / delete lines to implement my test.
EDIT : code updated with logs and counter
var SOURCE_PARENT_FOLDER_ID = '0B3qSFd3iikE3MS0yMzU4YjQ4NC04NjQxLTQyYmEtYTExNC1lMWVhNTZiMjlhMmI'
var MAX_RUNNING_TIME = 5*35*6;
function launchProcessFolder() {
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty('SOURCE_PARENT_FOLDER_KEY', SOURCE_PARENT_FOLDER_ID);
scriptProperties.setProperty('counter', 0);
scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
ScriptApp.newTrigger('continueProcess').timeBased().everyMinutes(10).create();
continueProcessFolder();
}
function continueProcessFolder() {
var startTime = (new Date()).getTime();
var scriptProperties = PropertiesService.getScriptProperties();
var srcParentFolderId = scriptProperties.getProperty('SOURCE_PARENT_FOLDER_KEY');
var continuationToken = scriptProperties.getProperty('CONTINUATION_TOKEN_KEY');
var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFolders() : DriveApp.continueFolderIterator(continuationToken);
var timeLimitIsNear = false;
var currTime;
var counter = Number(scriptProperties.getProperty('counter'));
while (iterator.hasNext() && !timeLimitIsNear) {
var folder = iterator.next();
counter++;
Logger.log(counter+' - '+folder.getName());
currTime = (new Date()).getTime();
timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);
if (!iterator.hasNext()) {
scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
ScriptApp.deleteTrigger(ScriptApp.getProjectTriggers()[0]);
Logger.log('******************no more folders**************');
break;
}
}
if(timeLimitIsNear){
var contToken = iterator.getContinuationToken();
scriptProperties.setProperty('CONTINUATION_TOKEN_KEY', contToken);
scriptProperties.setProperty('counter', counter);
Logger.log('write to scriptProperties');
}
}
EDIT 2 :
(see also last comment)
Here is a test with the script modified to get files in a folder. From my different tests it appears that the operation is very fast and that I needed to set a quite short timeout limit to make it happen before reaching the end of the list.
I added a couple of Logger.log() and a counter to see exactly what was happening and to know for sure what was interrupting the while loop.
With the current values I can see that it works as expected, the first (and second) break happens with time limitation and the logger confirms that the token is written. On a third run I can see that all files have been dumped.
var SOURCE_PARENT_FOLDER_ID = '0B3qSFd3iikE3MS0yMzU4YjQ4NC04NjQxLTQyYmEtYTExNC1lMWVhNTZiMjlhMmI'
var MAX_RUNNING_TIME = 5*35*6;
function launchProcess() {
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty('SOURCE_PARENT_FOLDER_KEY', SOURCE_PARENT_FOLDER_ID);
scriptProperties.setProperty('counter', 0);
scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
ScriptApp.newTrigger('continueProcess').timeBased().everyMinutes(10).create();
continueProcess();
}
function continueProcess() {
var startTime = (new Date()).getTime();
var scriptProperties = PropertiesService.getScriptProperties();
var srcParentFolderId = scriptProperties.getProperty('SOURCE_PARENT_FOLDER_KEY');
var continuationToken = scriptProperties.getProperty('CONTINUATION_TOKEN_KEY');
var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFiles() : DriveApp.continueFileIterator(continuationToken);
var timeLimitIsNear = false;
var currTime;
var counter = Number(scriptProperties.getProperty('counter'));
while (iterator.hasNext() && !timeLimitIsNear) {
var file = iterator.next();
counter++;
Logger.log(counter+' - '+file.getName());
currTime = (new Date()).getTime();
timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);
if (!iterator.hasNext()) {
scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
ScriptApp.deleteTrigger(ScriptApp.getProjectTriggers()[0]);
Logger.log('******************no more files**************');
break;
}
}
if(timeLimitIsNear){
var contToken = iterator.getContinuationToken();
scriptProperties.setProperty('CONTINUATION_TOKEN_KEY', contToken);
scriptProperties.setProperty('counter', counter);
Logger.log('write to scriptProperties');
}
}
As of January 1, 2016 this is still a problem. The bug report lists a solution using the Advanced Drive API, which is documented here, under "Listing folders".
If you don't want to use Advanced services, an alternative solution would be to use the Folder Iterator to make an array of File Ids.
It appears to me that the Folder Iterator misbehaves only when created using DriveApp.continueFolderIterator(). When using this method, only 100 Folders are included in the returned Folder Iterator.
Using DriveApp.getFolders() and only getting Folder Ids, I am able to iterate through 694 folders in 2.734 seconds, according the Execution transcript.
function allFolderIds() {
var folders = DriveApp.getFolders(),
ids = [];
while (folders.hasNext()) {
var id = folders.next().getId();
ids.push(id);
}
Logger.log('Total folders: %s', ids.length);
return ids;
}
I used the returned array to work my way through all the folders, using a trigger. The Id array is too big to save in the cache, so I created a temp file and used the cache to save the temp file Id.
This is caused by a bug in GAS:
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4116
It appears you're only storing a single continuation token. If you want to recursively iterate over a set of folders and allow the script to pause at any point (e.g. to avoid the timeout) and resume later, you'll need to store a bunch more continuation tokens (e.g. in an array of objects).
I've outlined a template that you can use here to get it working properly. This worked with thousands of nested files over the course of 30+ runs perfectly.

using and modifying global variables within handler functions

Hello everyone out there,
I can use global variables within a handler function, however I cannot modify them "globally" within than function.
In code below, after the first click it will show the number 1001 (the handler reads, increments and shows the right result).
But, any further clicks will always show 1001, so the handler keeps reading the original globalVar value: it doesn't get modified as I was expecting.
Anything I can do to fix this?
var globalVar = 1000;
function testingGlobals() {
var app = UiApp.createApplication();
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel().setId('panel');
app.add(panel);
panel.add(app.createButton(globalVar).setId("globalVar").addClickHandler(app.createServerHandler("chgGlobal").addCallbackElement(panel)));
doc.show(app)
}
function chgGlobal(e) {
var app = UiApp.createApplication();
globalVar++;
app.getElementById("globalVar").setText(globalVar);
return app;
}
You can not increment Global Variables this way, as every time a handler executes, Global variable is initialized and then manipulated.
You can use hidden formfields to hold the a variable which you can change in handler, and it will be persistent as long as the app is open.
e.g
function testingGlobals() {
var app = UiApp.createApplication();
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel().setId('panel');
var myVar = app.createHidden().setValue('0').setName('myVar').setId('myVar');
panel.add(myVar);
app.add(panel);
panel.add(app.createButton('0').setId("globalVar").addClickHandler(app.createServerHandler("chgGlobal").addCallbackElement(panel)));
doc.show(app)
}
function chgGlobal(e) {
var app = UiApp.createApplication();
gloabalVar = parseInt(e.parameter.myVar);
gloabalVar ++;
app.getElementById('myVar').setValue(gloabalVar.toString());
app.getElementById("globalVar").setText(gloabalVar);
return app;
}
You can persist data in CacheService (fast but not guaranteed) or ScriptProperties or ScriptDb (a little slower, but persisted) instead of global properties. ScriptDb in particular can hold objects without needing to use strings to store them.