1

I am trying to add in links to a menu in Google sheets using Google Script (JavaScript), based on some criteria. Google's API template for adding menu items is below:

addItem(caption, functionName)

The functionName parameter can't itself take input parameters, and since I have a variable number of worksheets within my document, I am trying to generate arbitrary number functions in a for loop, so that they may be available without needing any input parameters:

function createGotoMenuFunctions(scope) {
  var ss = SpreadsheetApp.getActive();
  var sheets = ss.getSheets();

  for (var s=0; s<sheets.length; s++){
    var sht = sheets[s];
    var shtName = sht.getName();
    var funcName = 'goto' + shtName.replace(/\s/g, '_') + 'menu';
    scope[funcName] =  function() {
      ss.setActiveSheet(sht);
    }
  }
}

createGotoMenuFunctions(this); //global scope

This creates a set of functions for each sheet named (for example) gotoSheet1menu(), gotoSheet2menu(), gotoSheet3menu(). However, when I call any of the functions, they all activate the last sheet. For example, if I call gotoSheet1menu(), it activates Sheet3. I believe this is due to ss and sht getting passed by reference instead of by value. Is there some way to fix this?

castoridae
  • 62
  • 6
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – 4castle Jul 21 '17 at 23:09
  • 1
    If `sheets` is an array, use `sheets.forEach` with a callback function (instead of that `for` loop), and it will create the closure that you need in order to make sure `sht` references the right thing. – 4castle Jul 21 '17 at 23:11
  • Really nicely asked first question @castoridae, however, if referring to an API or documentation, please provide link(s), so we're all _on the same page_; thanks :-) – Fred Gandt Jul 21 '17 at 23:14
  • Welcome to StackOverflow, and congrats on your first question. Remember to accept the best answer below that answers your question. – Joan Rieu Jul 21 '17 at 23:53

2 Answers2

0

createGotoMenuFunctions closure will keep one copy of sht which is the last sheet because it declare at the end. when function execute it will not find sht in its own closure and will approach closure which is enclosing it. So it will get sht (last sheet copy).

For correct answer you should use bind it just after function:

scope[funcName].bind({sht: sheets[s]})

Kaps
  • 189
  • 7
0

Here is also a way to bind sht to the closure:

scope[funcName] = function(_sht) {
  return function() {
    ss.setActiveSheet(_sht);
  }
}(sht);

(See this question, it was similar: javascript closure immediate evaluation )

Joel
  • 2,374
  • 17
  • 26