Vince
Vince

Reputation: 4232

Google Apps Script (V8); why can't I use an object instance inside of onOpen?

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

Answers (2)

chuckx
chuckx

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

Cooper
Cooper

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

Related Questions