Google Apps Script - Pass Anonymous Function To Menu.addItem

青春壹個敷衍的年華 提交于 2021-02-19 06:05:25

问题



I am building a container bound google apps script for a school. The school requires every written item to have the 'school heading'. The school uses blocks A-G as periods. My goal is that there will be a menu, 'School Heading', when opened it has the submenus 'A Block', 'B Block', 'C Block', and in each submenu there will be an option for each class, which will insert the heading for that class into the document's heading

Here is my code:

var BLOCKS = "abcdefg";
var CLASSES = ["English", "History", "Science", "Writing", "Latin", "Math", "Study Skills"];
var FUNCTION_NAMES;
var global = {};

init();

/**
 * The onOpen function runs automatically when the Google Docs document is
 * opened. Use it to add custom menus to Google Docs that allow the user to run
 * custom scripts. For more information, please consult the following two
 * resources.
 *
 * Extending Google Docs developer guide:
 *     https://developers.google.com/apps-script/guides/docs
 *
 * Document service reference documentation:
 *     https://developers.google.com/apps-script/reference/document/
 */
function onOpen() {
  init();
  // Add a menu with some items, some separators, and a sub-menu.
  var menu = DocumentApp.getUi().createMenu('School Heading')
  for(var i = 0; i < BLOCKS.length; i++){
    block = BLOCKS[i];
    Logger.log("Block: " + block)
    menu = menu.addSubMenu(DocumentApp.getUi().createMenu(block + " Block")
                           .addItem('English', 'eng' + block)
                           .addItem('History', 'his' + block)
                           .addItem('Science', 'sci' + block)
                           .addItem('Writing', 'wri' + block)
                           .addItem('Latin', 'lat' + block)
                           .addItem('Math', 'mat'  + block) 
                           .addItem('Study Skills', 'stu' + block));
    defineFunctions(block, this);
  }
  menu.addToUi();
}

function getFunc(class,block){
  return function(){
    createHeading(class,block);
  }
}

function defineFunctions(block, global){
  Logger.log(FUNCTION_NAMES)
  for(var i = 0; i < FUNCTION_NAMES.length; i++){
    var funcName = FUNCTION_NAMES[i] + block;
    eval("function " + funcName + " () { createHeading('"+ CLASSES[i] + "', '" + block + "'); }");
  }
}

function createHeading(class, block){
  var header = DocumentApp.getActiveDocument().getHeader();
  if(!header){
    header = DocumentApp.getActiveDocument().addHeader();
  }
  header.insertParagraph(0, "Name\n{class}, Block {block}".replace("{class}", class).replace("{block}", block)).setAlignment(DocumentApp.HorizontalAlignment.RIGHT);
}

function init(){
  if(!Array.isArray(BLOCKS)){
    BLOCKS = BLOCKS.toUpperCase().split("");
  }
  if(!Array.isArray(CLASSES)){
    CLASSES = CLASSES.split("\n");
  }
  if(!Array.isArray(FUNCTION_NAMES) || FUNCTION_NAMES.length !== CLASSES.length){
    FUNCTION_NAMES = [];
    for(var i = 0; i < CLASSES.length; i++){
      FUNCTION_NAMES.push(CLASSES[i].toLowerCase().substring(0,3));
    }
  }
}

When I select School Heading > A Block > English I get 'Script function not found engA'

My Question is, why is eval not working, and is it possible for me to pass an anonymous function to Menu.addItem?


回答1:


Your eval() is defining functions inside a for loop in a function, so the scoping rules mean that they just aren't available outside of that scope. By moving that code outside of all other functions, the eval()'d functions will be available to every running instance of the script.

This works:

var BLOCKS = "abcdefg";
var CLASSES = ["English", "History", "Science", "Writing", "Latin", "Math", "Study Skills"];
var FUNCTION_NAMES;
var global = {};

init();
/// Moved
for(var _i = 0; _i < BLOCKS.length; _i++){
var _block = BLOCKS[_i];
  for(var _j = 0; _j < FUNCTION_NAMES.length; _j++){
    var _funcName = FUNCTION_NAMES[_j] + _block;
    eval("function " + _funcName + " () { createHeading('"+ CLASSES[_j] + "', '" + _block + "'); }");
  }
}
///
debugger;  // Pause in debugger (All the functions are there!)

/**
 * The onOpen function runs automatically when the Google Docs document is
 * opened. Use it to add custom menus to Google Docs that allow the user to run
 * custom scripts. For more information, please consult the following two
 * resources.
 *
 * Extending Google Docs developer guide:
 *     https://developers.google.com/apps-script/guides/docs
 *
 * Document service reference documentation:
 *     https://developers.google.com/apps-script/reference/document/
 */
function onOpen() {
  // Add a menu with some items, some separators, and a sub-menu.
  var menu = DocumentApp.getUi().createMenu('School Heading')
  for(var i = 0; i < BLOCKS.length; i++){
    block = BLOCKS[i];
    Logger.log("Block: " + block)
    menu = menu.addSubMenu(DocumentApp.getUi().createMenu(block + " Block")
                           .addItem('English', 'eng' + block)
                           .addItem('History', 'his' + block)
                           .addItem('Science', 'sci' + block)
                           .addItem('Writing', 'wri' + block)
                           .addItem('Latin', 'lat' + block)
                           .addItem('Math', 'mat'  + block) 
                           .addItem('Study Skills', 'stu' + block));
    //defineFunctions(block, this);
  }
  menu.addToUi();
}

function getFunc(class,block){
  return function(){
    createHeading(class,block);
  }
}

function defineFunctions(block, global){
  Logger.log(FUNCTION_NAMES)
  for(var i = 0; i < FUNCTION_NAMES.length; i++){
    var funcName = FUNCTION_NAMES[i] + block;
    eval("this[" + funcName + "] = function () { createHeading('"+ CLASSES[i] + "', '" + block + "'); }");
  }
  debugger;
}

function createHeading(class, block){
  var header = DocumentApp.getActiveDocument().getHeader();
  if(!header){
    header = DocumentApp.getActiveDocument().addHeader();
  }
  header.insertParagraph(0, "Name\n{class}, Block {block}".replace("{class}", class).replace("{block}", block)).setAlignment(DocumentApp.HorizontalAlignment.RIGHT);
}

function init(){
  if(!Array.isArray(BLOCKS)){
    BLOCKS = BLOCKS.toUpperCase().split("");
  }
  if(!Array.isArray(CLASSES)){
    CLASSES = CLASSES.split("\n");
  }
  if(!Array.isArray(FUNCTION_NAMES) || FUNCTION_NAMES.length !== CLASSES.length){
    FUNCTION_NAMES = [];
    for(var i = 0; i < CLASSES.length; i++){
      FUNCTION_NAMES.push(CLASSES[i].toLowerCase().substring(0,3));
    }
  }
}



回答2:


I believe eval may be working, but that there is no guarantee that its the same instance of the script that is executed when you invoke the menu. Google Apps Script can't be keeping all those function in memory till the time you are ready to invoke the menu, so by that time, you have a clean slate and all those function are no longer referenced.

It is not possible to pass anonymous function / parameters at this stage, its been classified as an enhancement : https://code.google.com/p/google-apps-script-issues/issues/detail?id=477 not much progress on it though.

Why don't u use a side bar instead of a menu and there you can create an UI with a selector, based on which the proper header can be inserted. Once you have that mechanism in place, you can use the side bar for other cool stuff.

https://developers.google.com/apps-script/guides/docs



来源:https://stackoverflow.com/questions/18803093/google-apps-script-pass-anonymous-function-to-menu-additem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!