0

I am trying to create a dashboard for my classroom (async starting, async duration, async testing)...

My problem is I dynamicly create functions with eval (I know it's the way to the dark side). Do those dynamic created functions also no longer live then the for loop who they were created in?

     function onOpen() {
      const FUNC_STR = 'course';
      var evalString = '';
      const response = Classroom.Courses.list();
      const courses = response.courses;
      for(var index  in courses) {
        const course = courses[index]
        evalString += 'function ' + FUNC_STR + course.id + '() { ' + FUNC_STR + '(' + course.id + ') }';
        eval(evalString);
      }
      const ui = SpreadsheetApp.getUi();
      var mymenu = ui.createMenu('Classroom');
      if (courses && courses.length > 0) {
        for (i = 0; i < courses.length; i++) {
          const course = courses[i];
          mymenu.addItem(course.name, FUNC_STR + course.id);
          }
        mymenu.addToUi();
        }
    }
    function course(id ) {
      SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Classroom.Courses.get(id).name);
    }
Frank R.
  • 23
  • 4
  • Rest assured, it's not a way to the dark side - it *is* the dark side: poor language tokens get stitched together into a Frankenstein monster, are evaluated to life, live a little to pass control to their dark spawn and die before the next iteration and become garbage to be collected. At the very least use `Function` constructor... But why would you want to create functions like this in 2020, is there any reason to do so? – Oleg Valter is with Ukraine Jun 19 '20 at 14:28
  • Joking aside, just [don't use eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Never_use_eval!). As functions are first-class citizens in JS, if you evaluate a string that represent function declaration, you get back a `Function` object. But unless it is referenced from outside of the loop, it will be garbage-collected eventually. – Oleg Valter is with Ukraine Jun 19 '20 at 14:29
  • 1
    What runtime are you using [tag:v8] (new) or [tag:rhino] (old)? – Rubén Jun 19 '20 at 23:18
  • 1
    Related : [Specify scope for eval() in JavaScript?](https://stackoverflow.com/q/9781285/1595451) – Rubén Jun 19 '20 at 23:21
  • 1
    Although I'm not sure whether I could correctly understand about your goal, from my guess of your goal, I proposed a modified script without using `eval` as an answer. Could you please confirm it? If I misunderstood your question and that was not the direction you expect, I apologize. – Tanaike Jun 20 '20 at 01:55

1 Answers1

1

I believe your goal as follows.

  • You want to create the custom menu on Google Spreadsheet when the Google Spreadsheet is opened.
  • You want to create the custom menu by dynamically creating the functions.

For this, how about this answer?

Modification points:

  • When the function is run from the custom menu, the function is required to be set to this of the script.
    • In your script, the functions of evalString += 'function ' + FUNC_STR + course.id + '() { ' + FUNC_STR + '(' + course.id + ') }'; is not set to this. So, when the function is run at the custom menu, the error occurs like "no functions".
  • In order to dynamically set the functions to the custom menu and run the function when the function is run at the custom menu, the function for installing the functions you want to run is required to be run, when the function is run at the custom menu.
    • In this case, it is not required to use eval.

When your script is modified, it becomes as follows. In this case, I propose the modified script by modifying the script of these answers. Ref1 and Ref2

Modified script:

Please copy and paste the following script to the script editor. And please run installFunctions() or reopen the Spreadsheet. By this, the custom menu is set. When you select the function from the custom menu, the function can be run.

installFunctions(); // This is required to run the function. So please don't remove this.
function onOpen() {} // This can be used as the simple trigger. So please don't remove this.

function installFunctions() {
  const FUNC_STR = 'course';
  const response = Classroom.Courses.list();
  const courses = response.courses;
  const menu = SpreadsheetApp.getUi().createMenu('Classroom');
  courses.forEach(e => {
    const functionName = `FUNC_STR${e.id}`;
    this[functionName] = () => course(e.id);  // Install the function.
    menu.addItem(e.name, functionName);
  })
  menu.addToUi();
}

// This is your function.
function course(id) {
  SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Classroom.Courses.get(id).name);
}

Note:

  • In this case, it supposes that courses of const courses = response.courses; has the correct values and Classroom.Courses.get(id).name works. If an error occurs at them, please test the script using the simple values.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165