问题
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