【问题标题】:Can a Google Sheets custom menu pass a variable to function?Google 表格自定义菜单可以将变量传递给函数吗?
【发布时间】:2017-10-02 15:46:48
【问题描述】:

我正在 Google 表格中创建一个菜单项,其中将列出一个工作目录并提取该用户的电子邮件地址,以便稍后用于处理 Google 表格中的员工记录。我试图让它像这样工作:

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  // Or DocumentApp or FormApp.
  var myapp = ui.createMenu('MyApp');
  var pullemp = myapp.addSubMenu(ui.createMenu('Pull Employee')
          .addItem('Nathaniel MacIver', menuItem2('nm@emailaddress.com')));
  myap.addToUi();
}

function menuItem2(email) {
  SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
     .alert('That email address is '+email);
}

现在,当我打开电子表格时,该项目立即触发,我得到下面的结果,这就是我想要的:

但我希望它在我单击菜单中的按钮时触发。事实上,当我点击我的菜单按钮时,我得到了错误:

我知道链接提到函数名需要是一个字符串值,但为什么它会在我需要的时候在加载时工作,然后在我按下按钮时失败?

【问题讨论】:

  • 我不认为你可以使用在菜单项中有参数的函数。
  • this 回答你的问题了吗?

标签: google-apps-script google-sheets


【解决方案1】:

在这里值得的是我努力通过一个论点它有点啰嗦但它有效:(显然灵感来自 Matthew Wolman 的回答)

准备字典词典

var strings = {};
strings["Item1"] = {'func':"function1",'arg':"yes"};
strings["Item2"] = {'func':"function2",'arg':"no"};
strings["Item3"] = {'func':"function3",'arg':"maybe"};

为每个使用参数“arg”调用 myGatewayFunction 的菜单项添加一个函数:

Object.keys(strings).forEach(function (name) {
  this[strings[name]['func']] = function () {
    myGatewayFunction(strings[name]['arg']);
 }
});

添加onOpen函数并添加所有菜单项:

function onOpen() {
  var ui = DocumentApp.getUi()
  var myMenu = ui.createMenu('Custom');

  Object.keys(strings).forEach(function (name) {
    myMenu.addItem(name,strings[name]['func'])
  });

  myMenu.addToUi();
}

添加一个由所有菜单项函数调用的函数,带参数:

function myGatewayFunction(arg){
  DocumentApp.getUi().alert(arg);
  }
}

【讨论】:

    【解决方案2】:

    虽然您不能通过 .addItem() 方法直接传递使用变量调用的函数,因为它只接受字符串,但您可以采取一些额外的步骤(相对于您尝试做的额外步骤)动态创建预先设置有动态变量的函数。如果您有一个包含所有电子邮件地址的全局数组,例如:

    var emails = ["email_2@email.com",
                  "email_2@email.com",
                  "email_2@email.com",
                  "email_2@email.com"]
    

    您可以使用“this”关键字创建一个完全以该电子邮件命名的自定义函数。

    “this”关键字非常重要。 'this' 是一个对象。这个对象(不是双关语)包含的键是您 GAS 项目中的每个功能。例如,如果您的项目中有三个名为 function1、function2 和 function3 的函数,则“this”对象将具有三个键,每个键分别命名为 function1、function2 和 function3,每个键都是函数本身。所以:

    遍历您的数组并使用括号表示法为每封电子邮件创建一个函数。括号表示法允许您使用包含点表示法不允许的字符的字符串调用对象键。我不能通过说 this.email_1@email.com 来创建对象,但我可以创建一个对象,例如 this["email_1@email.com"]:

    emails.forEach(function (email) {
       this[email] = function () {
        SpreadsheetApp.getUi().alert('That email address is '+email);
      }
    });
    

    此代码将在您的 GAS 项目中为每封电子邮件在服务器端创建一个新函数,该函数本身仅命名为电子邮件。对于视觉表示,理论上您的项目中应该有:

    function email_1@email.com () {
      SpreadsheetApp.getUi().alert('That email address is email_1@email.com');
    }
    
    function email_2@email.com () {
      SpreadsheetApp.getUi().alert('That email address is email_2@email.com');
    }
    
    function email_3@email.com () {
      SpreadsheetApp.getUi().alert('That email address is email_3@email.com');
    }
    
    function email_4@email.com () {
      SpreadsheetApp.getUi().alert('That email address is email_4@email.com');
    }
    

    虽然您实际上不会看到这一点,但在运行函数时,这将是您的代码编译时在后端发生的情况。话虽如此,然后您可以将这些相同的电子邮件名称作为字符串动态传递给 .addItem() 方法(在同一全局“电子邮件”数组上使用 forEach 循环),这是您可以传递的唯一变量类型进入这个方法。

    var ui = SpreadsheetApp.getUi();
    // Or DocumentApp or FormApp.
    var myapp = ui.createMenu('MyApp');
    var pullemp = myapp.addSubMenu(ui.createMenu('Pull Employee'))
    
    emails.forEach(function (email) {
     pullemp.addItem(email,email)
    });
    
    myapp.addToUi();
    

    当点击其中任何一项时,代码将尝试运行名为 email_1@email.com 的函数(或第二、第三或第四封电子邮件,以点击者为准)。因为您遍历了 'this' 对象并创建了四个以每封电子邮件命名的函数,所以您的项目将成功地通过其名称找到该函数并运行它。


    编辑:我知道您最初想使用人员的姓名而不是他们的电子邮件来创建菜单。上面的菜单将在菜单本身中显示他们的电子邮件。因此,要创建一个以他们的名字和各自的电子邮件作为功能的菜单,您需要一个对象。我的建议是创建一个对象,将人名作为键,并将他们的电子邮件分配给该键:

    var emails = {};
    emails["Nathaniel MacIver"] = "nm@emailaddress.com"
    emails["John Doe"] = "jd@emailaddress.com"
    emails["Jane Smith"] = "js@emailaddress.com"
    

    现在您可以使用 Object.keys(\\object name) 方法来循环访问该对象。请注意,Object.keys() 方法返回对象键的数组。这就是为什么下面的 forEach() 方法起作用的原因:

    Object.keys(emails).forEach(function (name) {
      this[emails[name]] = function () {
      SpreadsheetApp.getUi().alert('That email address is: '+emails[name])
     }
    });
    

    email 之所以是 emails[name] 是因为 'name' 本身就是键的实际名称,而 emails[name] 是存储在该键中的值。所以 'name' = "Nathaniel MacIver" 和 emails[name] = emails["Nathaniel MacIver"],正如你在上面看到的,被分配给他的电子邮件地址。

    现在您应用与上述相同的概念来创建菜单,但您循环遍历对象而不是数组,并在 addItem() 方法中分配菜单项,如下所示:

    var ui = SpreadsheetApp.getUi();
    // Or DocumentApp or FormApp.
    var myapp = ui.createMenu('MyApp');
    var pullemp = myapp.addSubMenu(ui.createMenu('Pull Employee'))
    
    Object.keys(emails).forEach(function (name) {
     pullemp.addItem(name,emails[name])
    });
    
    myapp.addToUi();
    

    这将产生与第一个代码示例完全相同的结果,但提供人员的姓名作为菜单项,而不是他们的电子邮件地址。

    请原谅我很长的回答!我刚刚发现这个是为了解决我自己的 Google 表格自定义菜单问题。我希望这有帮助!如果您对此有任何疑问,请随时与我联系!

    【讨论】:

      【解决方案3】:

      当您使用

      创建菜单项时
      .addItem('Nathaniel MacIver', menuItem2('nm@emailaddress.com'))
      

      使用参数“nm@emailaddress.com”调用函数menuItem2。这会导致您看到警报。该函数的返回值是未定义的(因为您不会从中返回任何内容)。因此,您最终会得到与

      相同的菜单项
      .addItem('Nathaniel MacIver', undefined)
      

      这显然不会做任何事情。

      method addItem 只接受一个函数名,它不允许向该函数传递参数。为了做你想做的事,你需要为每个人使用单独的函数,每个函数里面都有一个硬编码的电子邮件。

      【讨论】:

      • 我就是这么想的。很可笑,如果你问我的话。相反,我将尝试在工作表中创建下拉菜单,并在运行时使用函数来收集所选值。谢谢米歇尔!
      【解决方案4】:

      Matthew Wolman 的解决方案在 Google 表格中不起作用。一切都在创建时起作用, typeof 看到功能。但是不确定它是否超出范围或是否存在其他安全策略,但是当您使用动态功能调用菜单时会出现错误:

        Execution failed: Script function not found: 'dynfunc-item1'
      

      能够让动态菜单在using globally scoped function defs 中工作。

      Matthew Wolman 的回答与有效的方法之间的主要区别在于:

      /* generate all menuFunctions in immediatelly called closure */
      (function (globalScope) {
      for (var i = 0, l = items.length; i < l; i++)
          createMenuFunction(items[i])
      
      // you need to generate menuFunction via another function,
      // because that way you generate scope to keep param unchanged.
      // If you'll generate menuFunction right inside for loop,
      // all your menuFunctions will refer to same instance of var i,
      // which will be undefined after this closure terminates.
      function createMenuFunction(param) {
          // here we define new function inside global scope
          globalScope[MENU_ITEM_FUNCTION_PREFIX + param] = function() {
             alertNum(param);
          }
      }
      })(this) // here we passing global scope and call the closure
      

      【讨论】:

      • 它可能对你不起作用,因为你把他的解决方案放在onOpen() 中。他的脚本在任何功能之外保持“原样”。
      猜你喜欢
      • 1970-01-01
      • 2013-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-13
      相关资源
      最近更新 更多