Call function after UiApp is loaded (Javascript onLoad equivalent) - google-apps-script

I have a UiApp form that requires some spreadsheet data (slowish retrieval). What I'd like to achieve is load the view as usual and after it has been displayed do something like calling a server handler that gets the data and updates my view when it's done. Is that possible?
I looked around but I didn't find anything.
According to this, "function doGet(e) is the GWT equivalent of onLoad", but it's not in my case as I would like to dispay the page and only then do the slow part...

Stumbled on this which we could then use to implement a pseudo-onLoad event.
The setValue function on checkboxes has an optional parameter to fire the valueChanged handler. So you can programmatically trigger the valueChanged handler, which allows you to return the app (load it initially) while the handler is being invoked simultaneously.
function doGet() {
var app = UiApp.createApplication();
var label = app.createLabel('Loading the data from the spreadsheet.')
.setId('statusLabel')
app.add(label);
var handler = app.createServerHandler('loadData');
var chk = app.createCheckBox('test').addValueChangeHandler(handler).setVisible(false).setValue(true,true).setId('chk');
app.add(chk);
return app;
}
function loadData(e) {
var app = UiApp.getActiveApplication();
Utilities.sleep(3000); //placeholder for some function that takes a long time.
var label = app.getElementById('statusLabel');
label.setText("Loaded the data from the spreadsheet");
return app;
}

Several other widgets have setValue(value, fireEvents) methods, eg. TextBox, PasswordTextBox etc. It seems like there is nothing unique about the CheckBox widget in implementing this technique.

Related

Automatically close a Google Apps Script UiApp after five seconds

I want to automatically close this UiApp after a certain number of seconds:
function showConfirmationDialogue() {
var app = UiApp.createApplication().setHeight('80').setWidth('400');
app.setTitle('test');
var panel = app.createVerticalPanel();
app.add(panel);
var doc = SpreadsheetApp.getActive();
doc.show(app);
// this part doesn't seem to work
Utilities.sleep(5000);
app.close();
return app;
}
Thanks!
The Ui you create is shown when you call doc.show(app) and the only way you can update it or close it is to use a handler function that ends with a return app.
So it is not possible to do what you want from the same function that creates the UI since it is "returned" only one time.
I know only one trick that can achieve what you want that is using a handler trigger source that will call a closing handler function automatically using a "special" property of the checkBox widget. Here is the code, it uses a checkBox that you can of course make invisible in your final code.
function showConfirmationDialogue() {
var app = UiApp.createApplication().setHeight('80').setWidth('400');
app.setTitle('test');
var panel = app.createVerticalPanel();
app.add(panel);
var handler = app.createServerHandler('closeWindow');
var chk = app.createCheckBox('checkBox to set invisible in real function').setValue(false,true).addValueChangeHandler(handler);
app.add(chk);
chk.setValue(true,true)//.setVisible(false);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
function closeWindow(){
Utilities.sleep(5000);
var app = UiApp.getActiveApplication().close();
return app;
}
You can use the same procedure to modify the UiApp instance in any way, change a Label text, add a widget... anything you want.

How to use in a Server Handler an object (flextable) created in another function

In Google Apps Script I'm trying to build an app like the following:
in the doGet function I build the app
function doGet() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel().setId('panel');
var Table = _table_create(some arguments);
...
panel.add(Table);
app.add(panel);
return app;
}
The table is created by a function since I need to create several similar tables with some variable arguments during runtime and I can't predict when and how many. Thus I have a function _table_create that creates the table containing a button
function _table_create(arguments) {
var app = UiApp.getActiveApplication();
var Table = app.createFlexTable();
...
var handler1 = app.createServerHandler("handler");
handler1.addCallbackElement(Table);
Table.setWidget(x,y,app.createButton("button", handler1).setId("button_"+Table.getId()));
...
return Table
}
and I want to write an Handler linked to the button in the table that adds some widgets in the table.
function handler(e){
var app = UiApp.getActiveApplication();
var table_id = (e.parameter.source).split('_');
table_id = table_id[0];
var table = app.getElementById(table_id);
Logger.log(table.getType()); //// prints GENERIC
...
table.setWidget(x,y,app.createTextBox());
return app;
}
but when I click the button I get an error message that says it doesn't find a method setWidget(string,number,TextBox) for table.
I think the problem is in the fact I defined the callback element Table in a function that << dies >> after creating the table and this callback element get lost for the handler. Any suggestion would be appreciated. Thanks.
Your code seems to be ok, the error message is (for once) very explicit, the first argument is a string and it should be a number. (I don't know where x comes from, but it has become a string somewhere... maybe it's a e.parameter.something which are always strings )
simply try this code in your handler :
table.setWidget(Number(x),y,app.createTextBox());

Google Apps Script - Share widgets between functions

This is my first time working with Google apps scripts, and I'm a bit confused as to how to access widgets from multiple functions.
Basically, I'd like to have a button that updates a label widget. So the label has some default text, but then updates to show some other text after an 'Update' button is pressed.
From what I've read, the only things that can be passed into event handlers are objects with a setName method. A label widget doesn't have this, so what can I do to update the value of a widget in my doGet function from the other handler function?
Here is an idea of what I'd like to do (but can't get to work):
function doGet() {
var app = UiApp.createApplication();
// Create the label
var myLabel = app.createLabel('this is my label')
app.add(myLabel)
// Create the update button
var updateButton = app.createButton('Update Label');
app.add(updateButton)
// Assign the update button handler
var updateButtonHandler = app.createServerHandler('updateValues');
updateButton.addClickHandler(updateButtonHandler);
return app;
}
function updateValues() {
var app = UiApp.getActiveApplication();
// Update the label
app.myLabel.setLabel('This is my updated label')
return app;
}
I've been scouring the internet for hours trying to find a solution but can't seem to figure it out. Any suggestions?
What you mention about getting the value of a widget from an object name property is to GET the value of a widget, not to SET it. (in this case uppercase is not to "shout" but simply to get attention :-))
And the example of the Label is typically an example of a widget that you cannot read the value...
What you are looking for is a way to set widget value : you have to get the element by its ID : see example below in your updated code:
function doGet() {
var app = UiApp.createApplication();
// Create the label
var myLabel = app.createLabel('this is my label').setId('label');
app.add(myLabel)
// Create the update button
var updateButton = app.createButton('Update Label');
app.add(updateButton)
// Assign the update button handler
var updateButtonHandler = app.createServerHandler('updateValues');
updateButton.addClickHandler(updateButtonHandler);
return app;
}
function updateValues() {
var app = UiApp.getActiveApplication();
// Update the label
var label = app.getElementById('label').setText('This is my updated label');
return app;
}

using and modifying global variables within handler functions

Hello everyone out there,
I can use global variables within a handler function, however I cannot modify them "globally" within than function.
In code below, after the first click it will show the number 1001 (the handler reads, increments and shows the right result).
But, any further clicks will always show 1001, so the handler keeps reading the original globalVar value: it doesn't get modified as I was expecting.
Anything I can do to fix this?
var globalVar = 1000;
function testingGlobals() {
var app = UiApp.createApplication();
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel().setId('panel');
app.add(panel);
panel.add(app.createButton(globalVar).setId("globalVar").addClickHandler(app.createServerHandler("chgGlobal").addCallbackElement(panel)));
doc.show(app)
}
function chgGlobal(e) {
var app = UiApp.createApplication();
globalVar++;
app.getElementById("globalVar").setText(globalVar);
return app;
}
You can not increment Global Variables this way, as every time a handler executes, Global variable is initialized and then manipulated.
You can use hidden formfields to hold the a variable which you can change in handler, and it will be persistent as long as the app is open.
e.g
function testingGlobals() {
var app = UiApp.createApplication();
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel().setId('panel');
var myVar = app.createHidden().setValue('0').setName('myVar').setId('myVar');
panel.add(myVar);
app.add(panel);
panel.add(app.createButton('0').setId("globalVar").addClickHandler(app.createServerHandler("chgGlobal").addCallbackElement(panel)));
doc.show(app)
}
function chgGlobal(e) {
var app = UiApp.createApplication();
gloabalVar = parseInt(e.parameter.myVar);
gloabalVar ++;
app.getElementById('myVar').setValue(gloabalVar.toString());
app.getElementById("globalVar").setText(gloabalVar);
return app;
}
You can persist data in CacheService (fast but not guaranteed) or ScriptProperties or ScriptDb (a little slower, but persisted) instead of global properties. ScriptDb in particular can hold objects without needing to use strings to store them.

Unable to use list box with server handler to change text of label

If the title is confusing, hopefully this makes it more clear what I'm trying to do:
function doGet(e) {
var app = UiApp.createApplication();
var list = app.createListBox().setName('list');
list.addItem("this");
list.addItem("that");
list.addItem("they");
var handler = app.createServerHandler('foo').addCallbackElement(list);
list.addChangeHandler(handler);
var label = app.createLabel("test").setId('label');
var panel = app.createVerticalPanel();
panel.add(list);
panel.add(label);
app.add(panel);
return app;
}​
function foo(e) {
var app = UiApp.getActiveApplication();
var value = e.parameter.list;
var label = app.getElementById('label');
label.setText(value);
}
It doesn't call any errors. If I intentionally put an error in foo, I get an error message, so I'm assuming the handler is getting called and just isn't doing anything. This works in a spreadsheet if I just have it bring up a Browser.msgBox(value), so I know that much works.
I'm trying to use this in a program that will automatically update all the listboxes on the page based on what is selected in the first listbox. I've been able to change things like visibility using server handlers and app.getElementById, but only with radio buttons, not a list box. I'm clearly doing something wrong here, but it's not obvious what that is.
To have your changes to the UiApp updated you have to return the app on your handler, e.g.
//...
label.setText(value);
return app;
}