In my apps script for Google Docs I'm trying to convert a ModalDialog to a DialogBox so that I can take advantage of the addCloseHandler() on DialogBoxes.
My showModalDialog works with the following GS code:
function showSheetPicker() {
var html = HtmlService.createTemplateFromFile('sheetPicker').evaluate().
setHeight(400).setWidth(600);
DocumentApp.getUi().showModalDialog(html, "Select a Sheet");
}
However, when I tried converting that to use createDialogBox(), I came up with the following:
function showSheetPicker() {
var app = UiApp.createApplication();
var autoHide = false;
var modal = true;
var dialog = app.createDialogBox(autoHide, modal);
dialog.setPopupPosition(100, 100).setSize(650, 450).setHTML(html)
.setTitle("Select a Sheet").createCloseHandler(handlingTheClose).show();
return app;
}
This is producing an error that tells me: The script completed but the returned value is not a supported return type..
What would I need to do to convert my ModalDialog to a DialogBox so that I can take advantage of the addCloseHandler()?
This is an example of a doc showing a dialogBox but the result is quite disappointing...
function myFunction() {
var app = UiApp.createApplication();
var autoHide = false;
var modal = true;
var dialog = app.createDialogBox(autoHide, modal);
dialog.setPopupPosition(100, 100).setSize(650, 450)
.setTitle('hover me').setHTML('this is a test dialog');
app.add(dialog).setHeight(450).setWidth(650);
DocumentApp.getUi().showModelessDialog(app, "Select a Sheet")
}
I've never succeeded in using this widget... good luck.
As for the closeHandler, I doubt you can use it since you won't be able to close the inner dialogBox, you will actually close the ModelessDialog in this example... (good luck again... I give up ;-)
Related
I have some google script code in a spreadsheet like this:
function doGet(e) {
var app = UiApp.createApplication();
//////////////////////////////////////////////////////////
var buttonEntrata = app.createButton('Entrata');
app.add(buttonEntrata);
var labelEntrata = app.createLabel('Entrata!')
.setId('statusLabelEntrata')
.setVisible(false);
app.add(labelEntrata);
var handlerEntrata = app.createServerHandler('myClickHandlerEntrata');
buttonEntrata.addClickHandler(handlerEntrata);
/////////////////////////////////////////////////////////////
var buttonUscita = app.createButton('Uscita');
app.add(buttonUscita);
var labelUscita = app.createLabel('Uscita!')
.setId('statusLabelUscita')
.setVisible(false);
app.add(labelUscita);
var handlerUscita = app.createServerHandler('myClickHandlerUscita');
buttonUscita.addClickHandler(handlerUscita);
return app;
}
function myClickHandlerEntrata(e) {
var app = UiApp.getActiveApplication();
var labelEntrata = app.getElementById('statusLabelEntrata');
labelEntrata.setVisible(true);
entrata()
app.close();
return app;
}
function myClickHandlerUscita(e) {
var app = UiApp.getActiveApplication();
var labelUscita = app.getElementById('statusLabelUscita');
labelUscita.setVisible(true);
uscita()
app.close();
return app;
}
This creates me 2 buttons that do what they have to.
the problem is that when I call the page like this:
https://script.google.com/macros/s/example_example_example_example/dev
I get a really ugly page which id like to personalize, for example aligning the buttons in the center of the page, resizing them maybe dynamically based on the screen resolution, etc....
how can I do this since this is a script and not an html page?
does google script support something like css?
TNX :)
There are a lot of CSS equivalents (attributes have the same name but are written differently, using camelCase and no hyphens)that you can use in UiApp, the full list is here.
You can use it with setAttribute(key,value) or setAttributes({key:value,key:value}) , the second one allowing for any number of parameters, doc here.
UiApp has been deprecated. Try HTMLService Instead.
Example:
function doGet(){
var html=HtmlService.createHtmlOutputFromFile("name_of_html_file");
html.setTitle("Some_Title");
return html;
}
Note: When you give link to others, deploy and give the URL that ends with "/exec".
URL that ends with /dev is that only the owner of the app can use!
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.
I have a web-deployed form written in Google Apps Script with doGet and doPost. In the doPost, the code checks if the user has filled in the form correctly (e.g. not leaving certain things blank). If not, it highlights the things that need to be fixed and adds a warning label. If everything is all right, it writes the form data to a spreadsheet.
The problem is that it doesn't seem like doPost can be called again if the user fixes the problems.
Any thoughts? Thanks!
EDIT: I am using UiService
EDIT: Here is a very simplified version of the app:
function doGet(e) {
var app = UiApp.createApplication();
var mainForm = app.createFormPanel().setId('mainForm');
var formContent = app.createVerticalPanel().setId('formContent');
var userName = app.createTextBox().setId('userName').setName('userName');
var passport = app.createFileUpload().setName('passport');
var submitButton = app.createSubmitButton('submit here');
var submitButtonWarning = app.createLabel('Something is wrong.').setId('submitButtonWarning')
.setVisible(false);
formContent
.add(userName)
.add(passport)
.add(submitButton)
.add(submitButtonWarning);
mainForm.add(formContent);
app.add(mainForm);
return app;
}
function doPost(e) {
var app = UiApp.getActiveApplication();
var userName = e.parameter.userName;
var passport = e.parameter.passport;
if (userName == 'no') {
app.getElementById('submitButtonWarning').setVisible(true);
app.add(app.getElementById('formContent'));
return app;
} else {
app.getElementById('submitButtonWarning').setVisible(false);
app.add(app.getElementById('formContent'));
return app;
}
return app;
}
I think you are just adding the wrong UI element to the app in your doPost.
Instead of
app.add(app.getElementById('formContent'));
use
app.add(app.getElementById('mainForm'));
I would like to make Google Apps Script webapp that search spreadsheet instantly as you type in the TextBox.
The implementation I"m using now is following. For each key up event new search is lauched with the current search string. I'm using delay on the function that onKeyUp is calling and if the onKeyDown is called I try to kill the previous function call and just go with the new. This works sometimes but the search takes different time to complete so the last one to finish is not allways the right one.
I don't know how to solve this task in Google Apps script. This can be implemented using something like this but these functions are not awailable:
var timer;
function up(){
setTimeout(mySpreadsheetSearchFunction, 500);
}
function down(){
clearTimeout (timer);
}
This is the code of the current implementation that is not working correctly:
function up(){
var cache = CacheService.getPrivateCache();
var now = parseInt(cache.get('iterate'));
Utilities.sleep(500);
if(parseInt(cache.get('iterate')) !== parseInt(now)){
return;
}else{
search();
}
showInGui();
}
function down(){
var cache = CacheService.getPrivateCache();
cache.put('iterate', 1+parseInt(cache.get('iterate')));
}
The CacheService is probably wrong tool for this job, what could be beter? Is this the right way to implement something like this?
Not sure you have to use the cache feature to get a fast result... I made such a script that works pretty well, I use variants of it all the time ;-)
you can test it on this test sheet
here is how it works, the script is maybe quite long but look only at the handler part (click)
On other version I read the spreadsheet outside of the function so the array of data becomes a global variable and mostly to limit the number of spreadsheet calls (it can hit the quota limit in this version sometimes)
// G. Variables
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lastrow = ss.getLastRow();
// ...
function onOpen() {
var menuEntries = [ {name: "Search GUI", functionName: "searchUI"},
];
ss.addMenu("Search Utilities",menuEntries);// custom menu
}
// Build a simple UI to enter search item and show results + activate result's row
function searchUI() {
var app = UiApp.createApplication().setHeight(130).setWidth(400);
app.setTitle("Search by name / lastname / adress");
var panel = app.createVerticalPanel();
var txtBox = app.createTextBox().setFocus(true);
var label=app.createLabel(" Item to search for :")
panel.add(label);
txtBox.setId("item").setName("item");
var label0=app.createLabel("Row").setWidth("40");
var label1=app.createLabel("Name").setWidth("120");
var label2=app.createLabel("Lastname").setWidth("120");
var label3=app.createLabel("Street").setWidth("120");
var hpanel = app.createHorizontalPanel();
hpanel.add(label0).add(label1).add(label2).add(label3)
//
var txt0=app.createTextBox().setId("lab0").setName("0").setWidth("40");
var txt1=app.createTextBox().setId("lab1").setName("txt1").setWidth("120");
var txt2=app.createTextBox().setId("lab2").setName("txt2").setWidth("120");
var txt3=app.createTextBox().setId("lab3").setName("txt3").setWidth("120");
var hpanel2 = app.createHorizontalPanel();
hpanel2.add(txt0).add(txt1).add(txt2).add(txt3)
var hidden = app.createHidden().setName("hidden").setId("hidden");
var subbtn = app.createButton("next ?").setId("next").setWidth("250");
panel.add(txtBox);
panel.add(subbtn);
panel.add(hidden);
panel.add(hpanel);
panel.add(hpanel2);
var keyHandler = app.createServerHandler("click");
txtBox.addKeyUpHandler(keyHandler)
keyHandler.addCallbackElement(panel);
//
var submitHandler = app.createServerHandler("next");
subbtn.addClickHandler(submitHandler);
submitHandler.addCallbackElement(panel);
//
app.add(panel);
ss.show(app);
}
//
function click(e){
var row=ss.getActiveRange().getRowIndex();
var app = UiApp.getActiveApplication();
var txtBox = app.getElementById("item");
var subbtn = app.getElementById("next").setText("next ?")
var txt0=app.getElementById("lab0").setText('--');
var txt1=app.getElementById("lab1").setText('no match').setStyleAttribute("background", "white");// default value to start with
var txt2=app.getElementById("lab2").setText('');
var txt3=app.getElementById("lab3").setText('');
var item=e.parameter.item.toLowerCase(); // item to search for
var hidden=app.getElementById("hidden")
var data = sh.getRange(2,2,lastrow,3).getValues();// get the 3 columns of data
for(nn=0;nn<data.length;++nn){ ;// iterate trough
Logger.log(data[nn])
if(data[nn].toString().toLowerCase().match(item.toString())==item.toString()&&item!=''){;// if a match is found in one of the 3 fields, break the loop and show results
txt0.setText(nn+2);
txt1.setText(data[nn][0]).setStyleAttribute("background", "cyan");
txt2.setText(data[nn][1]);
txt3.setText(data[nn][2]);
sh.getRange(nn+2,2).activate();
subbtn.setText("found '"+item+"' in row "+Number(nn+2)+", next ?");
hidden.setValue(nn.toString())
break
}
}
return app ;// update UI
}
function next(e){
var row=ss.getActiveRange().getRowIndex();
var app = UiApp.getActiveApplication();
var txtBox = app.getElementById("item");
var subbtn = app.getElementById("next").setText("no other match")
var hidden=app.getElementById("hidden");
var start=Number(e.parameter.hidden)+1;//returns the last search index stored in the UI
var item=e.parameter.item.toLowerCase(); // item to search for
var txt0=app.getElementById("lab0");
var txt1=app.getElementById("lab1").setStyleAttribute("background", "yellow");
var txt2=app.getElementById("lab2");
var txt3=app.getElementById("lab3");
var data = sh.getRange(2,2,lastrow,3).getValues();// get the 3 columns of data
for(nn=start;nn<data.length;++nn){ ;// iterate trough
if(data[nn].toString().toLowerCase().match(item.toString())==item.toString()&&item!=''){;// if a match is found in one of the 3 fields, break the loop and show results
txt0.setText(nn+2);
txt1.setText(data[nn][0]).setStyleAttribute("background", "cyan");
txt2.setText(data[nn][1]);
txt3.setText(data[nn][2]);
sh.getRange(nn+2,2).activate();
subbtn.setText("found '"+item+"' in row "+Number(nn+2)+", next ?");
hidden.setValue(nn.toString())
break
}
}
return app ;// update UI
}
Using the HtmlService you can instead implement this in pure HTML and JavaScript. This gives you the ability to load the spreadsheet data once, and then do the search client-side, where the performance should be better.
I have a simple application where the user press a button and a welcome message is shown. I need this message(label) to be hidden after few seconds, say 5 secs.
I couldn't find a function like setTimeout() in google apps script.
Can someone give an idea how I could implement this?
(as you can see, not an experienced programmer).
Thanks!!
A possible solution. The logic is to have two the button click handlers. the 1st one makes the label visible and the 2nd one sleeps 5 seconds and after hides the label.
function doGet(e) {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var btn = app.createButton().setText('Test');
var lblVisible = app.createLabel('Visible Test').setVisible(false).setId('lblVisible');
panel.add(btn);
panel.add(lblVisible);
var handler = app.createServerHandler('onBtnClick');
var handlerWait = app.createServerHandler('onWaitEvent');
handler.addCallbackElement(panel);
handlerWait.addCallbackElement(panel);
btn.addClickHandler(handler);
btn.addClickHandler(handlerWait);
app.add(panel);
return app;
}
function onWaitEvent(e) {
Utilities.sleep(5 * 1000);
var app = UiApp.getActiveApplication();
var lblVisible = app.getElementById('lblVisible');
lblVisible.setVisible(false);
return app;
}
function onBtnClick(e) {
var app = UiApp.getActiveApplication();
var lblVisible = app.getElementById('lblVisible');
lblVisible.setVisible(true);
return app;
}
Progress indicator solution etc, this works. It lets you chain events, I have used it several times.
Updating a widget value on the go. productforums.google.com/d/topic/apps-script/lABoP-cJcGQ/…