Google Apps脚本:动态创建电子表格菜单项

时间:2015-07-21 13:07:05

标签: google-apps-script google-sheets

我正在尝试使用Google Apps脚本在Google表格中创建动态填充的菜单。

  1. 我有一张表,' Classes',我列出了我教的课程。
  2. 在运行我的脚本时,我会让我的脚本读取并将这些类加载到数组中。
  3. 为了仅对原始'类中的值进行硬编码。表单我想为每个类创建一个子菜单项。
  4. 该表格被称为“类”'。 类表中的值是8H,9p1,9p2等。 它们位于A1:A12单元格中。 在调试器中,数组menuItemArray正确地加载了来自'类'的所有预期类。片材。

    我得到的错误是:

      

    TypeError:在对象9p1中找不到函数addSubMenu。 (第13行,   文件"代码")

    这是踏入行

    的时候
    menuItemArrayClass =  menuItemArray [menuCount]
    

    如果我做错了什么或者有更好的方法,我会非常感激。

    这是我的代码:

    function onOpen(e) {
        var ui = SpreadsheetApp.getUi(); 
        var menuCount = 0; 
        ui.createMenu('Manage Timetable')
        .addItem('First item', 'menuItem1')
        .addSeparator()
    
        var menuItemArray =     SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Classes').getDataRange().getValues();  
        for (menuCount=1;menuCount < menuItemArray.length;++menuCount) {
            var menuItemArrayClass = [] 
            menuItemArrayClass =  menuItemArray [menuCount]
            .addSubMenu(ui.createMenu('Manage Classes')
                .addItem(menuItemArrayClass [menuCount] + 'Schedule Timetable', 'runBatch1'))
            .addToUi();
        }     
    }
    

3 个答案:

答案 0 :(得分:2)

试试这个:

function onOpen(e) {
    var ui = SpreadsheetApp.getUi(); 
    var menuCount = 0; 
   var menu = ui.createMenu('Manage Timetable');
    menu.addItem('First item', 'menuItem1')
    menu.addSeparator()

    var menuItemArray =     SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Лист2').getDataRange().getValues();  
    for (menuCount=0;menuCount < menuItemArray.length;menuCount++) {
        var menuItemArrayClass = [] 
        menuItemArrayClass =  menuItemArray [menuCount];
        menu.addSubMenu(ui.createMenu('Manage Classes')
            .addItem(menuItemArrayClass + ' Schedule Timetable', 'runBatch1'))
        menu.addToUi();
    }     
}

如果从1开始,你会松开第一行,所以我把它改为0.

此外,您现在需要动态分配脚本,但我不知道它们是如何在工作表中设置的,所以我保留了该部分。

也不确定您是否每次都需要添加“管理课程”菜单,但我保留以防万一。

请尝试使用此功能,因为它看起来更像您想要的内容:

function onOpen(e) {
  var ui = SpreadsheetApp.getUi(); 
  var menuCount = 0; 
  var menu = ui.createMenu('Manage Timetable')
  .addItem('First item', 'menuItem1')
  .addSeparator();
  var subMenu = ui.createMenu('Manage Classes');

  var menuItemArray =     SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Лист2').getDataRange().getValues();  
  for (menuCount=0;menuCount < menuItemArray.length;menuCount++) {
    var menuItemArrayClass = [] 
    menuItemArrayClass =  menuItemArray [menuCount];
    subMenu.addItem(menuItemArrayClass + ' Schedule Timetable', 'runBatch1');      
  }     

  menu.addSubMenu(subMenu).addToUi();
}

答案 1 :(得分:1)

  • 您要创建自定义菜单。
  • 您要通过编辑电子表格来更新自定义功能。
  • 您要在自定义菜单中为多个功能使用一个功能名称,例如myFunction()
  • 当您从自定义菜单运行函数时,您希望将每个值都作为函数的参数。
  • 示例情况如下。
    • “ A”至“ I”列中有值。
    • 自定义菜单中有9个功能。打开电子表格时,将创建自定义菜单。函数名称与每个列名称相对应。
    • 单击“ A”列的功能时,将激活“ A”列的值。
    • 将“ I”列复制到“ H”列时,新功能将添加到自定义菜单。

我理解如上。如果我的理解是正确的,那么这个答案呢?请认为这只是几个可能的答案之一。

问题和解决方法:

不幸的是,在当前阶段,当使用addItem方法向自定义菜单添加功能时,无法使用该参数。并且,当运行自定义菜单中的功能之一时,无法检索到有关运行的功能名称的信息。这样,您的目标无法直接实现。因此,需要使用替代方法。

当我看到您的问题时,出于您的目标,我认为this thread是有用的。在google.script.run,要求能够直接在脚本编辑器上运行该函数,并且该函数包含在this中。但是在自定义菜单上,当功能包含在this中时,即使无法直接在脚本编辑器中运行该功能,也可以运行该功能。当仅在GAS端运行该功能时,即使无法使用脚本编辑器直接运行该功能,也可以运行该功能。我认为这种情况可以用于解决方法。

修改后的脚本:

通过包含此替代方法来修改脚本时,脚本将如下所示。请将其复制并粘贴到Spreadsheet的容器​​绑定脚本中,该脚本的第一行具有标头(“ Col1”,“ Col2”,“,”),第二行具有相应的值。并且当您运行脚本时,请打开电子表格。这样,添加了自定义菜单。并且当通过复制添加新列时,附加列也将添加到自定义菜单。并且运行自定义菜单上的功能时,将激活与该列对应的值。

发件人:

function onOpen(e) {
  var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var headers = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
  var ui = SpreadsheetApp.getUi();
  var menu = ui.createMenu('Custom Menu')
  .addItem('First item', 'menuItem1')
  .addSeparator();
  var subMenu = ui.createMenu('Sub-menu');
  for (var i = 0; i < headers.length; i++) {
    var dynamicMenu = headers[i];
    subMenu.addItem(dynamicMenu,'dynamicItem');
  }
  menu.addSubMenu(subMenu).addToUi();
}

function onEdit(e) {
  onOpen(e);
}

function menuItem1() {
  SpreadsheetApp.getUi()
  .alert('You clicked the first menu item!');
}

function dynamicItem() {
  SpreadsheetApp.getUi()
  .alert('You clicked the dynamic menu item!');
}

收件人:

function installFunctions() {
  var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var headers = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
  var ui = SpreadsheetApp.getUi();
  var menu = ui.createMenu('Custom Menu')
  .addItem('First item', 'menuItem1')
  .addSeparator();
  var subMenu = ui.createMenu('Sub-menu');
  for (var i = 0; i < headers.length; i++) {
    var dynamicMenu = headers[i];
    this[dynamicMenu] = dynamicItem(i); // Added
    subMenu.addItem(dynamicMenu,dynamicMenu); // Modified
  }
  menu.addSubMenu(subMenu).addToUi();
}

function dynamicItem(i) { // Added
  return function() {
    var sheet = SpreadsheetApp.getActiveSheet();
    sheet.getRange(2, i + 1, sheet.getLastRow() - 1, 1).activate();
  }
}

installFunctions(); // Added

function onOpen() {} // Modified: This can be used as the simple trigger.

function onEdit() {} // Modified: This can be used as the simple trigger.

function onChange() {} // Added: Please install OnChange event trigger to this function.
  • 在使用此脚本之前,请将OnChange事件触发器安装到onChange()的功能上。这样,当删除列时,将更新自定义菜单。
  • function onEdit() {}function onChange() {}的功能用于运行onOpen();

结果:

enter image description here

注意:

  • 为了动态创建自定义菜单,该脚本需要在运行该功能的初始阶段运行。因此,必须像onOpen();一样将其放在全局位置。
  • 在此替代方法中,运行函数时,每次运行onOpen。因此,当列数很大时,处理成本将很高。所以请注意这一点。
  • 这是一个简单的示例脚本,用于解释一种解决方法。因此,请根据您的情况进行修改。

参考:

答案 2 :(得分:0)

假设您要将相关的标题和函数名称分别存储在Sheet1的A和B列中,只需调用.getValues()并遍历该数据即可。

/** 
 * SHEET1 VALUES
 * --------------------------------------
 *          Column A        Column B
 * Row 1    Class One       functionOne
 * Row 2    Class Two       functionTwo
 * Row 3    Class Three     functionThree
 */

function onOpen(e) {
  // Initialize the menu & submenu
  var ui = SpreadsheetApp.getUi(); 
  var menu = ui.createMenu('Menu Name').addItem('First label', 'menuItem1').addSeparator();
  var subMenu = ui.createMenu('Submenu Name');

  // Get the submenu item data from Sheet1
  // Values in column A represent the captions that will appear in the submenu
  // Values in column B represent the name of the function to be called
  var values = SpreadsheetApp.getActive().getSheetByName('Sheet1').getRange('A:B').getValues();
  for (var rowIndex = 0; rowIndex < values.length; ++rowIndex) {
    var rowData = values[rowIndex];
    var caption = rowData[0]; // Column A value
    var functionName = rowData[1]; // Column B value
    if (caption != '' && functionName != '') { // Exclude empty strings
      subMenu.addItem(caption, functionName);
    }
  }     

  // Add the submenu to the menu, and finally to the UI
  menu.addSubMenu(subMenu).addToUi();
}

function menuItem1() { Browser.msgBox('menuItem1'); }
function functionOne() { Browser.msgBox('functionOne'); }
function functionTwo() { Browser.msgBox('functionTwo'); }
function functionThree() { Browser.msgBox('functionThree'); }

在原始帖子中,发生错误是因为在值[9p1]而不是.addSubMenu()上调用了Menu object。此外,根据我对问题的理解,我认为应该在循环中创建菜单 item ,而不是每个类的单独子菜单。因此,这是两个需要解决的问题。

我还假定您不想为每个菜单项都运行runBatch1,因为这样做会破坏创建不同菜单项的目的。根据{{​​3}},添加菜单项的方法需要两个字符串:

  1. caption –菜单项的标签。
  2. functionName –用户选择项目时要调用的函数的名称。

因此,可以在进行submenu.addItem()调用时替换任何字符串。

var caption = "Caption";
var functionName = "functionName";
var subMenu = ui.createMenu('Submenu Name');
subMenu.addItem(caption, functionName);     

其他情况

项目对象数组

或者,您可以定义要包含的各种子菜单项的数组(或对象图),而不是将其存储在电子表格中。在这里,我使用了一个对象数组来清楚地定义标题和functionName值。

function onOpen(e) {
  // Initialize the menu & submenu
  var ui = SpreadsheetApp.getUi(); 
  var menu = ui.createMenu('Menu Name').addItem('First label', 'menuItem1').addSeparator();
  var subMenu = ui.createMenu('Submenu Name');

  // Array of items to include in the submenu
  var items = [
    { caption: 'Class One', functionName: 'functionOne' },
    { caption: 'Class Two', functionName: 'functionTwo' },
    { caption: 'Class Three', functionName: 'functionThree' }
  ];
  for (var i = 0; i < items.length; ++i) {
    var item = items[i];
    subMenu.addItem(item.caption, item.functionName);

  }     

  // Add the submenu to the menu, and finally to the UI
  menu.addSubMenu(subMenu).addToUi();
}

function menuItem1() { Browser.msgBox('menuItem1'); }
function functionOne() { Browser.msgBox('functionOne'); }
function functionTwo() { Browser.msgBox('functionTwo'); }
function functionThree() { Browser.msgBox('functionThree'); }

字幕字符串操作

由于该方法接受任何字符串,因此您可以执行任何将导致有效函数名的字符串操作。在此示例中,我仅将标题存储在数组中,并根据标题值动态生成函数名称。 (您也可以从A列中提取标题名称,并应用相同的操作。)

function onOpen(e) {
  // Initialize the menu & submenu
  var ui = SpreadsheetApp.getUi(); 
  var menu = ui.createMenu('Menu Name').addItem('First label', 'menuItem1').addSeparator();
  var subMenu = ui.createMenu('Submenu Name');

  // Array of captions to include in the submenu.
  // Will generate function names from this.
  var captions = ['Class One', 'Class Two', 'Class Three'];
  for (var i = 0; i < captions.length; ++i) {
    var caption = captions[i];
    subMenu.addItem(caption, 'function' +  caption.split(' ')[1]);
  }     

  // Add the submenu to the menu, and finally to the UI
  menu.addSubMenu(subMenu).addToUi();
}

function menuItem1() { Browser.msgBox('menuItem1'); }
function functionOne() { Browser.msgBox('functionOne'); }
function functionTwo() { Browser.msgBox('functionTwo'); }
function functionThree() { Browser.msgBox('functionThree'); }