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.
Related
I built an app in which I use ScriptProperties to store data from a handler to its but(e) function. This was working nice, until other people started using the same spreadsheet at the same time. So often happens that one person is taking a time thinking about what item choose from a checkbox menu and another person uses the same function, changing the data stored at scriptProperties and affecting the use of the function by the first person.
What is the best way to fix it, using an alternative way to pass information through the handler?
Here one sample of one of theese functions (in which I'm using ScriptProperties to pass the values ofletterSpreadsheetId and recipientArray):
function letter(letterSpreadsheetId){
ScriptProperties.setProperty('letterSpreadsheetId', letterSpreadsheetId); // different people may have different letterSpreadsheetId;
ScriptProperties.setProperty('letter', 1); // to be used in another function
var activeSheet = ss.getActiveSheet();
var app = UiApp.createApplication().setHeight(400).setWidth(600);
var panel = app.createVerticalPanel(); // you can embed that in a form panel
var label = app.createLabel("Choose a receiver").setStyleAttribute("fontSize", 18);
app.add(label);
var sheet = SpreadsheetApp.openById(letterSpreadsheetId).getSheetByName("receivers");
var recipientArray = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("receiver"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientArray.length); i++){
listBox.addItem(recipientArray[i][1]);
}
item3Panel.add(listBox);
var recipientArrayStr = JSON.stringify(recipientArray);
ScriptProperties.setProperty('recipientArr', recipientArrayStr);
var handlerBut = app.createServerHandler("butAnswerLetter").addCallbackElement(panel);
var but = app.createButton("submit").setId("submitButton2").addClickHandler(handlerBut);
panel.add(item1Panel)
.add(item2Panel)
.add(item3Panel)
.add(but)
.add(app.createLabel().setId("answer"));
var scroll = app.createScrollPanel().setPixelSize(600, 400).setTitle("My title 1");
scroll.add(panel);
app.add(scroll);
ss.show(app);
}
function butAnswerLetter(e){
var letterSpreadsheetId = ScriptProperties.getProperty('letterSpreadsheetId');
var recipient = e.parameter.item3;
ScriptProperties.setProperty('recipient', recipient);
var recipientArrayRecovery = ScriptProperties.getProperty('recipientArr');
var recipientArray = JSON.parse(recipientArrayRecovery);
for(var i=0;i<recipientArray.length;i++){
if(recipient == recipientArray[i][1]){
var usedRecipientArray = recipientArray[i];
}
}
You have 2 possibilities (that I know), either use userProperties instead of script-Properties as these are associated with the user but it will require the user to login and authorize, or - and this will work in every case even if the app is accessed anonymously, use the tags that you can write on almost any widget.
the syntax is quite simple, here is a small code example :
function doGet(){
var app = UiApp.createApplication().setTitle('test_TAG');
var list = app.createListBox(true).setVisibleItemCount(5).setPixelSize(30,450).setName('list');
var handler = app.createServerHandler('show').addCallbackElement(list);
list.addChangeHandler(handler);
var data = [];
for(var n = 0;n<20;n++){
list.addItem(n+' ');
data.push('available value = '+Number(n+1));
}
list.setTag(data.toString());
app.add(list);
return app
}
function show(e){
var app = UiApp.getActiveApplication();
var data = e.parameter.list_tag.split()
var selected = e.parameter.list;
app.add(app.createTextBox().setText(selected).setPixelSize(200,20));
app.add(app.createTextArea().setText(data.join()).setPixelSize(200,300));
return app;
}
testable here
Edit
following Zig's pertinent comment :
I forgot to mention the hidden widget (or a textBox / area set to invisible, useful for debugging when you want to check what it contains ! ) that is also useable of course...
The comment about a user having multiple windows showing the same app is also worth mentioning !
All in all you have 3 possibilities after all !
(thanks to Zig Mandel)
I am trying to create a menu in a listbox format where user chooses an option and then another uiapp is shown with the info they selected. I am having an issue here that when I opened google gives me an error that says Error encountered. An expected error occurred. I think it has to do with the setId part, if I remove one of the setId's the error doesnt happen. is this even possible?
function doGet(e) {
var app = UiApp.createApplication().setTitle("Services");
var dropDownList = app.createListBox().setName('list').setId('list');
var infoLabel = app.createLabel('Scroll around to select the service desired').setId('infoLabel');
var panel = app.createVerticalPanel();
//addItem fills the list
dropDownList.addItem("Option 1").setId("add");
dropDownList.addItem("Option 2");
panel.add(dropDownList);
panel.add(infoLabel);
app.add(panel);
var info = app.getElementById("add");
var handler2 = app.createServerHandler('display2');
info.addClickHandler(handler2);
app.add(dropDownList);
app.add(infoLabel);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
function display2(e) {
var app = UiApp.createApplication();
var html = app.add(app.createHTML("<p><p><b>You have selected this option</b> </p>")).setHeight(220).setWidth(220);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
return app;
}
Are you deploying this as a web app or a script within a Spreadsheet?
If you are deploying this as a web app, then SpreadsheetApp.getActiveSpreadsheet() will not work - replace it with SpreadsheetApp.openById(id) where id is your spreadsheet ID which you will find in the URL when you open the file in the browser.
If you want to deploy this w/in a spreadsheet through a menu item or a simple button, then that works as is.
I was able to just copy paste your code and get the listbox part working fine -
https://docs.google.com/spreadsheet/ccc?key=0AkJNj_IM2wiPdHRYQThlaGVVSk04R052ZGNqclhEZWc#gid=0
Update -
I now understand what you are trying to do. Couple of things - you want to make sure you are adding a callback element via handler.addCallbackElement(myWidget) otherwise, you will not be able to read the value of the element. Second thing is that you don't need a server handler on each option in a dropdown list. Just having one handler will fire it for every change and you'll be able to get the option you selected.
I've cleaned up the code here below and also updated the spreadsheet to use this code.
function showUI() {
var app = UiApp.createApplication().setTitle("GeekSquad Services");
var infoLabel = app.createLabel('Scroll around to select the service desired');
var dropDownList = app.createListBox().setName('list').setId('list');
dropDownList.addItem("Option 1");
dropDownList.addItem("Option 2");
//you can add as many options here manually or dynamically
var handler = app.createServerHandler('dropDownCallback')
handler.addCallbackElement(dropDownList);
dropDownList.addClickHandler(handler);
var panel = app.createVerticalPanel();
panel.add(dropDownList);
panel.add(infoLabel);
app.add(panel);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
function dropDownCallback(e) {
var app = UiApp.createApplication();
var html = app.add(app.createHTML("<b>You have selected this option</b> " + e.parameter.list));
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
Update #2 -
If you want to fork off and create different app instances thats easy (though its unclear why wouldn't just change panels).
function dropDownCallback(e) {
if(e.parameter.list === 'Option 1'){
var app = UiApp.createApplication();
var html = app.add(app.createHTML("Here for option!"));
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
}
else if (e.parameter.list ==== 'Option 2'){
//create and show other App here or whatever else
}
//refactor this better to not repeat code.
}
On the code below I am defining a TextBox with name and id. The button handler works fine but I am not been able to get the value entered on the TextBox. The msgBox shows up but the e.parameter.matchValue shows as undefined.
On other part of the app I have the same logic but with a ListBox and it works fine.
What am I doing wrong?
function chooseColumnValueToMatch() {
var app = UiApp.createApplication().setHeight(150).setWidth(250);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var columnName = ScriptProperties.getProperty("columnNameToMatch");
var h1Line = app.createHTML("<h2>Value to match "+columnName+":</h2>");
var textPara = app.createHTML("<p>Type the VALUE that the <b>"+columnName+"</b> column must match EXACTLY to send the message.</p>");
var selectionHandler = app.createServerHandler('chooseColumnValueToMatchSelectionHandler');
var valueInputBox = app.createTextBox()
.setTitle("Match value for "+columnName)
.setName("matchValue")
.setId("matchValue");
var submitBtn = app.createButton("Submit", selectionHandler);
app.add(h1Line)
.add(textPara)
.add(valueInputBox)
.add(submitBtn);
ss.show(app);
}
function chooseColumnValueToMatchSelectionHandler(e){
var app = UiApp.getActiveApplication();
var columnName = ScriptProperties.getProperty("columnNameToMatch");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var msg = e.parameter.matchValue;
Browser.msgBox("Value to match "+columnName+" column is:", msg, Browser.Buttons.OK);
return app;
}
You have to use the ServerHandler.addCallbackElement method. The following code demonstrates it. The method call "tells" GAS internals that the value of the widget pointed as the parameter should be passed to the handler.
If you have multiple widgets, which should be handled by the handler, then, instead of calling the addCallbackElement with every widget, is possible to place all controls to a panel and point only the panel as the addCallbackElement method parameter. The code in the point 4.4 of this tutorial shows this way.
function doGet(e) {
var app = UiApp.createApplication();
var valueInputBox = app.createTextBox()
.setTitle("Match value for XXX")
.setName("matchValue")
.setId("matchValue");
var selectionHandler = app.createServerHandler('chooseColumnValueToMatchSelectionHandler');
selectionHandler.addCallbackElement(valueInputBox);
var submitBtn = app.createButton("Submit", selectionHandler);
var output = app.createLabel().setId("output");
app.add(valueInputBox).add(submitBtn).add(output);
return app;
}
function chooseColumnValueToMatchSelectionHandler(e){
var app = UiApp.getActiveApplication();
var output = app.getElementById("output");
var msg = e.parameter.matchValue;
output.setText("Value to match XXXX column is: " + msg);
return app;
}
It seems that you forgot to add a callbackElement to your handler
you could try like this :
selectionHandler.addCallbackElement(valueInputBox)
right after the submitBtn definition
I've built a form with UiApp to collect information from the user. It's rather complex with multiple panels and file uploads, so I would like to give the user the opportunity review their inputs before submitting. I was hoping to display their inputs on one final review panel that would then allow them to decide to edit the info and move back to a earlier panel to edit.
Following is the test script. The farthest I've gotten is getting it to return 'textBox' and not the value of the textBox. Is it possible to get the values while staying in the doGet portion of my script, or must I move to doPost to access the values?
What would be the work around you would suggest?
Thanks for any and all help!
function doGet(e){
var app = UiApp.createApplication();
var appPanel = app.createVerticalPanel();
var form = app.createFormPanel();
var panel1 = app.createHorizontalPanel();
var emailLabel = app.createLabel('Your Email');
var email = app.createTextBox().setName('email').setId('email');
app.add(form);
var button1 = app.createButton('Go to Review');
panel1.add(emailLabel);
panel1.add(email);
panel1.add(button1);
appPanel.add(panel1);
form.add(appPanel);
var panel2 = app.createHorizontalPanel().setVisible(false);
var reviewLabel = app.createLabel('Your Email:');
var reviewEmail = app.createLabel(email);
panel2.add(reviewLabel);
panel2.add(reviewEmail);
appPanel.add(panel2);
//
var reviewPageTwo = app.createClientHandler()
.forTargets(panel1).setVisible(false)
.forTargets(panel2).setVisible(true);
button1.addClickHandler(reviewPageTwo);
return app;
}
UPDATE 8.24.12
I'm including the resulting script. It includes the review function, the button to lead the user back to edit, and the submitButton to post it. (You will need to replace the spreadsheet ID for the post to work.)
Thank for the help all!
Martin
function doGet(e){
var app = UiApp.createApplication();
var appPanel = app.createVerticalPanel();
var form = app.createFormPanel();
var panel1 = app.createHorizontalPanel().setId('panel1');
var emailLabel = app.createLabel('Your Email');
var email = app.createTextBox().setName('email').setId('email');
var syncChangeHandler = app.createServerHandler('syncText').addCallbackElement(form);
app.add(form);
var button1 = app.createButton('Go to Review');
panel1.add(emailLabel);
panel1.add(email);
panel1.add(button1);
appPanel.add(panel1);
form.add(appPanel);
var panel2 = app.createHorizontalPanel().setId('panel2').setVisible(false);
var reviewGrid = app.createGrid(3,3).setId('reviewGrid');
var reviewEmail = app.createLabel().setId('reviewEmail');
var reviewLabel = app.createLabel('Your Email:');
var submitButton = app.createSubmitButton('Submit');
var button2 = app.createButton('Edit Response');
panel2.add(reviewLabel);
panel2.add(reviewEmail);
panel2.add(button2);
panel2.add(submitButton);
appPanel.add(panel2);
//
var editResponse = app.createClientHandler()
.forTargets(panel1).setVisible(true)
.forTargets(panel2).setVisible(false);
button1.addClickHandler(syncChangeHandler);
button2.addClickHandler(editResponse);
return app;
}
function syncChangeHandler(e){
var app = UiApp.getActiveApplication();
app.getElementById('reviewEmail').setText(e.parameter.email);
app.getElementById('panel1').setVisible(false);
app.getElementById('panel2').setVisible(true);
return app;
}
function doPost(e){
var ss = SpreadsheetApp.openById('*your spreadsheet id here*').getSheets()[0];
var range = ss.getRange(ss.getLastRow()+1, 1, 1,2);
var values = [[new Date(),e.parameter.email]];
range.setValues(values);
var app = UiApp.getActiveApplication();
var label = app.createLabel('Thank You!');
app.add(label);
return app;
}
You have your entire function inside doGet(). The doGet() function is executed when your UI is first loaded.
So,
var email = app.createTextBox().setName('email').setId('email');
actually resolves to a text box. When you do
var reviewEmail = app.createLabel(email);
you are trying to pass a text box as an argument to createLabel, which is not allowed. Therefore this won't work. You must handle the changes to the text box in a handler.
function doGet(){
var syncChangeHandler = app.createServerHandler('syncText').addCallbackElement(form);
var email = app.createTextBox().setName('email').setId('email');
...
var reviewEmail = app.createLabel().setId('reviewEmail');
...
}
function syncText(e){
var app = UiApp.getActiveAplication();
app.getElementById('reviewEmail').setText(e.parameter.email);
return app;
}
What Srik said is true (of course ;-)), you can't indeed assign a label this type of value... What I would do (since you work in a doGet/doPost structure) is to create a second button just aside of the submit button that triggers a handler to a 'review' function that populates all the corresponding textBoxes , listBoxes or whatever you have with the values coming from your main form (a sort of copy of it) in the review panel that you already have. To achieve this you will need to add the form as a callBackElement to the handler (which was not necessary with the doPost scheme).
Another option could be to add this handler to all the widgets separately with 'key up' triggers or 'Value change triggers' so that the review panel is always up to date in real time, in this case the 'review before submit panel' could be visible at any time without further action from the user other than make it eventually visible (although it could also be always visible). In this option the handler function would be more like a 'synchroniser'. I'm afraid you'll have some difficulties with file upload though (since this can only work in a doGet/doPost structure).
I have 3 Radio buttons in my Ui in the same Radio group. They are,
var rbutton1 = app.createRadioButton('dist','5 miles');
var rbutton2 = app.createRadioButton('dist','10 miles');
var rbutton3 = app.createRadioButton('dist','25 miles');
In the event handler function, the variable, e.parameter.dist gives true or false just based on whether rbutton3 (the last radio button) is checked or not. Is there any way to determine what radio button is selected exactly?
The only way the make radio buttons group work like this (as intended by design) is by using them in a FormPanel and looking the name (in your case "dist") on a doPost from a submit action of the form.
There's some workarounds though, using the new client handlers that make it radio buttons usage on any panel roughly the same as on the from. Please take a look at this issue on the tracker. You may want to star this issue as well, to keep track of updates and kind of vote for it.
I use:
eventData.parameter.source
and pick up the change using addClickHandler.
You need to store this somewhere
Are these buttons suppose to be in an exclusive OR mode. If so, they need to have the same name. Look at Serge's answer for a detailed explanation and example code.
in the meantime I came up with a workaround to set the radiobutton as well, in this example I use a listBox but any other data could be used.
Here is the complete code : (to test in a spreadsheet container)
function radiotest() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var radioValue = app.createTextBox().setId('radioValue');
radioValue.setId("radioValue").setName("radioValue");
var listhandler = app.createServerHandler('listhandler').addCallbackElement(panel);
var list = app.createListBox().addChangeHandler(listhandler).setName('list');
for(var i = 1; i < 10; i++){
var name = 'choice '+i;
list.addItem('Activate '+name,name)
var handler = app.createClientHandler().forTargets(radioValue).setText(name);
panel.add(app.createRadioButton('radioButtonGroup',name).addValueChangeHandler(handler).setId(name));
}
panel.add(radioValue);
var getit=app.createButton("Valide").setId("val");
panel.add(getit).add(list)
var handler = app.createServerHandler("valide")
handler.addCallbackElement(panel)
getit.addClickHandler(handler);
app.add(panel);
SpreadsheetApp.getActiveSpreadsheet().show(app);// show app
}
//
function valide(e){ ;// This function is called when key "validate" is pressed
var sh = SpreadsheetApp.getActiveSheet();
var RadioButton = e.parameter.radioValue;
sh.getRange('A1').setValue(RadioButton);
var app = UiApp.getActiveApplication();
return app;
}
function listhandler(e){ ;// This function is called when listBox is changed
var sh = SpreadsheetApp.getActiveSheet();
var app = UiApp.getActiveApplication();
var listvalue = e.parameter.list
var radioValue = app.getElementById('radioValue').setValue(listvalue)
sh.getRange('A2').setValue(listvalue);
var radiobutton = app.getElementById(listvalue)
radiobutton.setValue(true)
return app;
}
the selected radioButton values comes in the textBox value and the listBox allows to select which radioButton is activated... it shows up like this
There is also another approach, as stated by eddyparkinson that is to use the e.parameter.source but this works only if the handler is assigned directly to the radioButton and not using a 'submit' button. In many case it can be used and makes the code a(little) bit lighter.
Here is a test of this code
function radiotest2() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var listhandler = app.createServerHandler('listhandler2').addCallbackElement(panel);
var list = app.createListBox().addChangeHandler(listhandler).setName('list');
var handler = app.createServerHandler("valide2")
handler.addCallbackElement(panel)
for(var i = 1; i < 10; i++){
var name = 'choice '+i;
list.addItem('Activate '+name,name)
panel.add(app.createRadioButton('radioButtonGroup',name).setId(name).addClickHandler(handler));
}
panel.add(list)
app.add(panel);
SpreadsheetApp.getActiveSpreadsheet().show(app);// show app
}
function valide2(e){ ;// This function is called when a radioButton is selected
var sh = SpreadsheetApp.getActiveSheet();
var source = e.parameter.source;
var radioValue = '';
if(source.match('choice')=='choice'){radioValue=source}
sh.getRange('A1').setValue(radioValue);
var app = UiApp.getActiveApplication();
return app;
}
function listhandler2(e){ ;// This function is called when listBox is changed
var sh = SpreadsheetApp.getActiveSheet();
var app = UiApp.getActiveApplication();
var listvalue = e.parameter.list
sh.getRange('A2').setValue(listvalue);
var radiobutton = app.getElementById(listvalue)
radiobutton.setValue(true)
return app;
}