Reputation: 4232
Why can't I use an object instance inside of onOpen
?
I'm experimenting with Google Apps Script using the new V8 runtime. I wrote some simple code which makes an instance of a class in the onOpen
function and tries to associate a call to a method on that instance when I click on a menu entry.
When I click the associated menu entry, I get a "Script function not found" error. However, when I either create a global instance of the class or create a local instance in another function it works fine.
I tried logging the value of either a local instance or a global instance, but it only shows an empty object: {}
.
Is this a bug, or some detail I missed while reading the documentation?
/** Application Class */
class Application {
/**
* ShowUi
*/
showUi() {
// const html = HtmlService.createHtmlOutputFromFile('Ui');
const html = HtmlService.createHtmlOutput('<h1>Hello World</h1>');
const ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, 'User Interface');
}
}
const global_app = new Application();
/** onOpen */
function onOpen() {
const app = new Application();
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('JSClass Example');
console.log('app, local scope:');
console.log(app);
console.log('app, global scope');
console.log(global_app);
menu.addItem('Show UI (local)', 'app.showUi');
menu.addItem('Show UI (global)', 'global_app.showUi');
menu.addItem('Show UI (global fn)', 'showUi');
menu.addToUi();
}
/** showUi */
function showUi() {
const app3 = new Application();
app3.showUi();
}
My bound Spreadsheet for this code is here. I think you'll have to make a copy and click through some scary-looking warnings to actually run it, though.
Upvotes: 1
Views: 543
Reputation: 6937
In your example, the onOpen()
method is executed solely to populate the menu, but not when a menu option is selected.
When you select a menu item, it's performed in a fresh execution context and the state from within the onOpen()
function from the previous execution (i.e. to populate the menu) is not carried over.
Another approach is using a static method, which you can call without having to create an instance of the class.
class Application {
static showUi() {
const html = HtmlService.createHtmlOutput('<h1>Hello World</h1>');
const ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, 'User Interface');
}
}
function onOpen() {
const app = new Application();
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('V8 Menu Test');
menu.addItem('Show UI', 'Application.showUi');
menu.addToUi();
}
Upvotes: 4
Reputation: 64100
I used an installable onOpen trigger but it also works on a simple trigger. I just wasn't paying attention to the menu when I first ran it.
/** Application Class */
class Application {
/**
* ShowUi
*/
showUi() {
// const html = HtmlService.createHtmlOutputFromFile('Ui');
const html = HtmlService.createHtmlOutput('<h1>Hello World</h1>');
SpreadsheetApp.getUi().showModalDialog(html, 'User Interface');
}
}
const global_app = new Application();
/** onOpen */
function openForMe() {
const app = new Application();
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('JSClass Example')
.addItem('Show UI (local)', 'app.showUi')//this is still not working
.addItem('Show UI (global)', 'global_app.showUi')
.addItem('Show UI (global fn)', 'showUi')
.addToUi();
}
/** showUi */
function showUi() {
const app3 = new Application();
app3.showUi();
}
This seems to be okay:
/** Application Class */
class Application {
showUi() {
const html = HtmlService.createHtmlOutput('<h1>Hello World</h1>');
SpreadsheetApp.getUi().showModalDialog(html, 'User Interface');
}
}
const global_app = new Application();
/** onOpen */
function onOpen(){
SpreadsheetApp.getUi().createMenu('JSClass Example')
.addItem('Show UI (global)', 'global_app.showUi')
.addItem('Show UI (global fn)', 'showUi')
.addToUi();
}
/** showUi */
function showUi() {
const app3 = new Application();
app3.showUi();
}
Upvotes: 1