66

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?

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Ryan
  • 10,041
  • 27
  • 91
  • 156

8 Answers8

134

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');
Vince
  • 3,962
  • 3
  • 33
  • 58
AshClarke
  • 2,990
  • 5
  • 21
  • 27
  • 2
    Your answer worked perfectly for me I used "File > Project properties > Script properties". I then added a "Property" and a "Value" and then I used your "retrieved with" code. Very nice! – Chris S Jan 23 '16 at 15:48
  • 4
    Nice! thanks! this only stores strings, so I used json strings to store complex objects. :) – Yotes Nov 21 '18 at 13:18
  • 1
    Remember that setting such a global variable in your script won't trigger a re-execution of custom functions in spreadsheet cells. So you can't use a global variable to for instance have a cell in the spreadsheet automatically update based on reading that global variable. – Magne May 20 '20 at 10:10
  • Very useful solution for me. – ManuAlvarado22 Dec 29 '21 at 21:51
  • 1
    Currently `setProperty` and `getProperty` are deprecated. What is the modern way of doing that? Using a Cell to store the info? – Sandburg Jan 13 '22 at 09:59
  • @Sandburg Google did not mark the methods as deprecated. Documentation here: https://developers.google.com/apps-script/guides/properties – Sami Boudoukha Nov 18 '22 at 14:37
  • In the GUI, those so-called "Script Properties" are now in the Apps Script IDE at "Project Settings" (the Gear Icon on the left) at the bottom. – zonksoft May 08 '23 at 19:37
  • Just to add to the value of the conversation Im struggling storing json String as @Yotes did but, converting it back gives an error, since its storing the string in an odd maner, like: {data={key_people=[Ljava.lang.Object;@41c0e22b},... – Leandro Gamarra May 09 '23 at 19:14
13

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.

SoftwareTester
  • 1,048
  • 1
  • 10
  • 25
  • 1
    I doubt performance is affected when using a global versus local. Please detail. – Zig Mandel Jul 14 '14 at 18:02
  • I'm referring to the answer and discussion by Serge Insas http://stackoverflow.com/questions/24326524/addtimer-not-working-with-global-app-in-gas I also saw another discussion at SO regarding local vs global variables. But haven't found it now – SoftwareTester Jul 15 '14 at 06:17
  • 4
    Ok I see. But note the performance hit would be from calling code to initialize the variable, and not the fact that is a global or local. – Zig Mandel Jul 15 '14 at 13:01
10

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);
}
Magne
  • 16,401
  • 10
  • 68
  • 88
KrzFra
  • 129
  • 1
  • 2
  • 6
    For this to work it should be: `function globalVariables(){ var variables = { sheetName: 'Sheet1', variable1: 1, variable2: 2}; return variables; }` – Sandro Wiggers Sep 22 '17 at 19:03
  • @SandroWiggers Well spotted. I edited the code so it should work now. – Magne Apr 22 '20 at 13:17
  • 1
    This solution will not actually work as a global _variable_, since you cannot change the global "variable" by using for instance `globalVariables().sheetName = 'Sheet2'`. This is also the case if it was merely a seeming global variable like `var global = { sheetName: 'Sheet1' }`, since doing `global.sheetName = 'Sheet2'` inside a function will have no effect on that global variable, but merely create a local copy. Using the global variable in another function after that will not have picked up the change. See: https://stackoverflow.com/a/17375386/380607 – Magne May 22 '20 at 09:10
  • This also works with declaring global `const`. Quite a trick! – Snail-Horn Sep 26 '20 at 17:06
4

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);
};
Community
  • 1
  • 1
Tim
  • 756
  • 1
  • 7
  • 12
3

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);
Kambaa
  • 467
  • 3
  • 8
2

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

1

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);
    }
}


ΞΫΛL
  • 184
  • 7
-1
    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");

}
  • 8
    Please edit your answer to provide further explanation. In particular, what are the key aspects of your code that address the question? How does your approach differ from the other five answers? And why might a reader prefer your approach? Answering these questions will make this a much more useful answer for the community. Thank you. – Jeremy Caney Oct 03 '20 at 17:52