I have the following code where I try to fill a listbox with the addItem(text,value) method to store both a text and an index and I would like to retriebe both when the listbox is clicked.
As a default e.parameter.listObject returns the text. Is there anyway to retriebe also the value?
The code is as follows:
function doGet() {
var app = UiApp.createApplication().setTitle('New app');
var listHandler = app.createServerKeyHandler('listSelect');
var lb = app.createListBox(true).setId('lbColour').setName('lbColour').setVisibleItemCount(10).addClickHandler(listHandler);
lb.addItem("RED","100");
lb.addItem("GREEN","010");
lb.addItem("BLUE","001");
// Create a vertical panel and add the grid to the panel
var panel = app.createVerticalPanel();
panel.setId('mainPanel');
panel.add(lb);
panel.add(app.createLabel('Colour Code:'));
panel.add(app.createTextBox().setName('colourCode').setId('colourCode'));
panel.add(app.createLabel('Colour Name:'));
panel.add(app.createTextBox().setName('colourName').setId('colourName'));
app.add(panel);
return app;
}
function listSelect(e){
var app = UiApp.getActiveApplication();
app.getElementById('colourName').setText(e.parameter.lbColour);
app.getElementById('colourCode').setText(e.parameter['lbColour_Value']);
return(app);
}
Thanks to the below answers this will be the corrected code for the above one.
Note that as stated we can store the key at the text property using a valid separator, I used here "|" which is not as usual to find in an string as ",".
Also note that the main to problems on the original code for this to work was that the lisbox cannot be created as a multiselect, that is:
We shall use "createListBox()" instead of "createListBox(true)"
The server handler shall be created using "createServerHandler" instead of "creteServerKeyHandler".
The final code should look like this:
function doGet() {
var app = UiApp.createApplication().setTitle('New app');
var listHandler = app.createServerHandler('listSelect');
var lb = app.createListBox().setId('lbColour').setName('lbColour').setVisibleItemCount(10).addClickHandler(listHandler);
lb.addItem("RED","RED|100");
lb.addItem("GREEN","GREEN|010");
lb.addItem("BLUE","BLUE|001");
// Create a vertical panel and add the grid to the panel
var panel = app.createVerticalPanel();
panel.setId('mainPanel');
panel.add(lb);
panel.add(app.createLabel('Colour Code:'));
panel.add(app.createTextBox().setName('colourCode').setId('colourCode'));
panel.add(app.createLabel('Colour Name:'));
panel.add(app.createTextBox().setName('colourName').setId('colourName'));
app.add(panel);
return app;
}
function listSelect(e){
var app = UiApp.getActiveApplication();
var outpArr = e.parameter.lbColour.split('|');
app.getElementById('colourName').setText(outpArr[0]);
app.getElementById('colourCode').setText(outpArr[1]);
return(app);
}
You can't retrieve the text of your list box item.
What you can do is this,
Reformat your list items like this:
lb.addItem("BLUE","001,BLUE");
And then
e.parameter.lbColour.split(',')[0]
Will return 001
And
e.parameter.lbColour.split(',')[1]
Will return BLUE
Good luck
function doGet() {
var app = UiApp.createApplication();
var vp = app.createVerticalPanel().setId("vp");
var lb = app.createListBox().setName("lb");
lb.addItem("BLUE", "BLUE,001");
vp.add(lb);
var handler = app.createServerHandler('postFunc').addCallbackElement(vp);
var button = app.createButton("Send").addClickHandler(handler);
vp.add(button);
app.add(vp);
return app;
}
function postFunc(e){
var app = UiApp.getActiveApplication();
var vp = app.getElementById("vp");
var outpArr = e.parameter.lb.split(',');
var color = app.createLabel("color text: " + outpArr[0]);
var number = app.createLabel("color number: " + outpArr[1]);
vp.add(color);
vp.add(number);
return app;
}
EDIT 2 : Thomas's answer wasn't wrong after all... The handler used in the original question was the cause of the issue and the multiple selection enable suffers also of an issue -see last comments (issue 941).
So I removed my first answer (to make it shorter) and suggest here a possible workaround to use multiple selection on a listBox (tested and working)
function doGet() {
var app = UiApp.createApplication().setTitle('New app');
var listHandler = app.createServerHandler('listSelect');
var lb = app.createListBox(true).setWidth('200').setId('lbColour').setName('lbColour').setVisibleItemCount(5).addChangeHandler(listHandler);
var colorIndex = [];// this is the array that will hold item values values with 2 fields
lb.addItem("RED");colorIndex.push("RED-100");// use a '-' separator to be able to split later First field-second field
lb.addItem("GREEN");colorIndex.push("GREEN-010");
lb.addItem("BLUE");colorIndex.push("BLUE-001");
lb.setTag(colorIndex.toString());// store the array in the TAG
var panel = app.createVerticalPanel();
panel.setId('mainPanel');
panel.add(lb);
panel.add(app.createLabel('Colour Code:'));
panel.add(app.createTextBox().setWidth('500').setName('colourCode').setId('colourCode'));
panel.add(app.createLabel('Colour Name:'));
panel.add(app.createTextBox().setWidth('500').setName('colourName').setId('colourName'));
app.add(panel);
return app;
}
function listSelect(e){
var app = UiApp.getActiveApplication();
var codepos = [];//will get the index position of items in colorIndex
var code = [];//array of first items found in colorIndex
var color = [];// index of second items found in colorIndex
var colorIndex = e.parameter.lbColour_tag.split(',');// recover the array
for(n=0;n<colorIndex.length;++n){if(e.parameter.lbColour.match(colorIndex[n].split('-')[0]) == colorIndex[n].split('-')[0]){codepos.push(n)}};// get the position in the array
for(n=0;n<codepos.length;++n){
code[n] = colorIndex[codepos[n]].split('-')[1];// finally get the color code value
color[n] = colorIndex[codepos[n]].split('-')[0];// get the color value
}
app.getElementById('colourName').setText(color.toString());// show the result second field
app.getElementById('colourCode').setText(code.toString()); // show the result first field
return(app);
}
Related
I'm trying to create an app script to work inside a Google Site that has a search text box which is supposed to reference a Spreadsheet and spit out results from that search. The problem, I believe, exists in the second half of the code where I'm trying to reference the spreadsheet based on the text entered into the box provide by the code below.
function doGet(e) {
var doc = SpreadsheetApp.openById(SPREADSHEET_ID_GOES_HERE);
var app = UiApp.createApplication().setTitle('New app');
// Create the entry form, a 1 x 2 grid with text boxes for name, age, and city that is then added to a vertical panel
var grid = app.createGrid(1, 2);
grid.setWidget(0, 0, app.createLabel('Name:'));
grid.setWidget(0, 1, app.createTextBox().setName('userName').setId('userName'));
// Create a vertical panel and add the grid to the panel
var panel = app.createVerticalPanel();
panel.add(grid);
var buttonPanel = app.createHorizontalPanel();
var button = app.createButton('submit');
var submitHandler = app.createServerClickHandler('submit');
submitHandler.addCallbackElement(grid);
button.addClickHandler(submitHandler);
buttonPanel.add(button);
var closeButton = app.createButton('close');
var closeHandler = app.createServerClickHandler('close');
closeButton.addClickHandler(closeHandler);
buttonPanel.add(closeButton);
// Create label called statusLabel and make it invisible; add buttonPanel and statusLabel to the main display panel.
var statusLabel = app.createLabel().setId('status').setVisible(false);
panel.add(statusLabel);
panel.add(buttonPanel);
app.add(panel);
return app;
}
function close() {
var app = UiApp.getActiveApplication();
app.close();
return app;
}
The Problem exists in the code below. I'm trying to search the Spreadsheet using the text entered into the textBox. I'm not sure how to achieve this.
// function called when submit button is clicked
function submit(e) {
// Write the data in the text boxes back to the Spreadsheet
var cell = getValue(e.parameter.submit);
var doc = SpreadsheetApp.openById('SPREADSHEET_ID_GOES_HERE');
var ss = doc.getSheets()[0];
var lastRow = doc.getLastRow();
var data = ss.getRange(2, 1, 2, 4).getValues();
for(nn=0;nn<data.length;++nn){
if (data[nn][1]==cell){break} ;// if a match in column B is found, break the loop
}
// Make the status line visible and tell the user the possible actions
app.getElementById('status').setVisible(true).setText(data[nn][1]);
return app;
}
When the form is submitted, your handler function submit(e) receives an event, e. If we log the value of it with Logger.log(Utilities.jsonStringify(e)); we'll see that looks like this:
{"parameter":
{
"clientY":"52",
"clientX":"16",
"eventType":"click",
"ctrl":"false",
"meta":"false",
"source":"u146139935837",
"button":"1",
"alt":"false",
"userName":"Lucy",
"screenY":"137",
"screenX":"16",
"shift":"false",
"y":"14",
"x":"12"
}
}
We can access the value of our input boxes by name, for example e.parameter.userName.
One other tweak, we need to get a value for app. Here's what your working handler looks like:
// function called when submit button is clicked
function submit(e) {
var app = UiApp.getActiveApplication();
Logger.log(Utilities.jsonStringify(e)); // Log the input parameter (temporary)
// Write the data in the text boxes back to the Spreadsheet
var cell = e.parameter.userName;
var doc = SpreadsheetApp.openById('SPREADSHEET-ID');
var ss = doc.getSheets()[0];
var lastRow = doc.getLastRow();
var data = ss.getRange(2, 1, 2, 4).getValues();
var result = "User not found";
for (nn = 0; nn < data.length; ++nn) {
if (data[nn][1] == cell) {
result = data[nn][1];
break
}; // if a match in column B is found, break the loop
}
// Make the status line visible and tell the user the possible actions
app.getElementById('status').setVisible(true).setText(result);
return app;
}
My script is iterating through some spreadsheet information. I read the documentation and the scrollpanel is only allowed one child. I therefore wrapped the horizontal panel information inside a scrollpanel but for some reason the scrollpanel never shows up with contents just the colored background. Any ideas why this may be?
var myscrollpanel = app.createScrollPanel().setPixelSize(100, 100);
myscrollpanel.setWidth("100%");
myscrollpanel.setStyleAttribute("background", "silver");
var vpanel = app.createVerticalPanel();
for (i=1; i <= mylastrow;++i)
{
var cellsname= mydatarange.getCell(i,1).getValue().toLowerCase();
// Browser.msgBox(cellsname );
// Browser.msgBox(searchstr.toString());
if (cellsname.toString() == searchstr.toString())
{
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(mydatarange.getCell(i,5).getValue());
sbehavior.setText(mydatarange.getCell(i,8).getValue());
sconsequence.setText(mydatarange.getCell(i,6).getValue());
scomment.setText(mydatarange.getCell(i,10).getValue());
steacher.setText(mydatarange.getCell(i,7).getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);
app.add(panelrow);
cnt = ++cnt;
}
}
// Browser.msgBox(cnt);
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
// return myscrollpanel;
return app ;// update UI
there are a few "anomalies" in your code that could cause some issues, I suggest a couple of changes (see code below).
Another point is that it is not a good idea to read a spreadsheet in a loop, it is mush more efficient to read the whole range in one time and then use the array values to iterate.
Here is a code proposal, I didn't test it (and it will probably need some debugging) but put a few comments to explain the changes.
var myscrollpanel = app.createScrollPanel()//.setPixelSize(100, 100);// 100 pixel is quite small to put all the items you add !!
myscrollpanel.setWidth("100%");//
myscrollpanel.setStyleAttribute("background", "silver");// this will not affect the widgets you add to the panel...
var vpanel = app.createVerticalPanel();
var data = mydatarange.getValues();// get all data in an array
for (i=0; i <data.length;++i) // and work directly with array values (indexed to 0 instead of cells indexed to 1)
{
var cellsname= data[i][0].toString().toLowerCase();
if (cellsname == searchstr.toString()){
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(data[i][4].getValue());
sbehavior.setText(data[i][7].getValue());
sconsequence.setText(data[i][5].getValue());
scomment.setText(data[i][9].getValue());
steacher.setText(data[i][6].getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);// add only to the panel, not to the app or the row will be outside the panel
cnt = ++cnt;
}
}
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
return app ;// update UI
}
I'm trying to write a generic server handler for textboxes which highlights the text when the textbox gains focus:
function onFocusHighlight(e) {
var app = UiApp.getActiveApplication();
var widget = app.getElementById(e.parameter.source);
var widgetValue = e.parameter.widgetName; // how can I get widgetName from source???
widget.setSelectionRange(0, widgetValue.length);
return app;
}
Can I determine widgetValue from e.parameter.source?
var widget = app.getElementById(e.parameter.source).setName("widgetName")
Then get widgetValue:
var widgetValue = e.parameter.widgetName
Check https://developers.google.com/apps-script/uiapp for usage of setName.
I've found that as long as widgetName and widgetId are the same I can determine widgetValue as follows:
function onFocusHighlight(e) {
var app = UiApp.getActiveApplication();
var widgetId = e.parameter.source;
var widgetName = widgetId; // name MUST match id to retrieve widget value
var widgetValue = e.parameter[widgetName]; // not sure why this syntax works???
var widget = app.getElementById(widgetId);
widget.setSelectionRange(0,widgetValue.length);
return app;
}
My only issue now is understanding how/why the e.parameter[widgetName] syntax actually works. It would also be great to have a solution which doesn't rely on widgetName and widgetId being the same value.
Just a suggestion following your own answer :
You could probably make it work by having a conversion table somewhere that gets the widgets name from their ID.
For example you could define :
ScriptProperties.setProperties({'widgetID1':'Name1','widgetID2':'name2'},true)
and then
widgetvalue = e.parameter[ScriptProperties.getProperty(e.parameter.source)]
I didn't test this code but it seems logical ;-), tell me if it doesn't (I don't have time to test it now)
EDIT : works as expected, here is a test sheet to show the result, test code below.
function Test(){
var app = UiApp.createApplication().setTitle('test');
app.add(app.createLabel('Type anything in upper textBox and then move your mouse over it...'))
var p = app.createVerticalPanel()
var txt1 = app.createTextBox().setId('txt1').setName('Name1').setValue('value in TextBox1')
var txt2 = app.createTextBox().setId('txt2').setName('Name2').setValue('waiting to mouseOver textBox1')
p.add(txt1).add(txt2)
app.add(p)
var handler = app.createServerHandler('mouseOver').addCallbackElement(p);
txt1.addMouseOverHandler(handler);
ScriptProperties.setProperties({'txt1':'Name1','txt2':'Name2'},true);//save the name, only txt1 will be used here
var ss=SpreadsheetApp.getActiveSpreadsheet()
ss.show(app)
}
function mouseOver(e) {
var app = UiApp.getActiveApplication();
var widget = app.getElementById(e.parameter.source);
var widgetValue = e.parameter[ScriptProperties.getProperty(e.parameter.source)]; // ScriptProperties knows the Widget's name
app.getElementById('txt2').setValue(widgetValue) ;// Use the other widget to show result
return app;
}
Could someone please show me how to make the last item added to the flextable go directly underneath the existing item?
function doGet() {
var app = UiApp.createApplication();
var listBox = app.createListBox();
listBox.addItem("item 1").addItem("item 2").addItem("item 3").setName("myListBox");
var handler = app.createServerHandler("buttonHandler");
// pass the listbox into the handler function as a parameter
handler.addCallbackElement(listBox);
var table = app.createFlexTable().setId("myTable");
var button = app.createButton("+", handler);
app.add(listBox);
app.add(button);
app.add(table);
return app;
}
function buttonHandler(e) {
var app = UiApp.getActiveApplication();
app.getElementById("myTable").insertRow(0).insertCell( 0, 0).setText( 0, 0, e.parameter.myListBox);
return app;
}
The idea is to keep in memory the index of the row you write to. One can use many ways to achieve this but the most straightforward is probably to write this row index in the table or listBox tag, something like this (I didn't test the code but hope I made no error):
EDIT : the tag solution didn't seem to work, so I changed to a hidden widget solution.(tested)
function doGet() {
var app = UiApp.createApplication();
var listBox = app.createListBox();
listBox.addItem("item 1").addItem("item 2").addItem("item 3").setName("myListBox");
var hidden = app.createHidden().setName('hidden').setId('hidden')
var handler = app.createServerHandler("buttonHandler");
// pass the listbox into the handler function as a parameter and the hidden widget as well
handler.addCallbackElement(listBox).addCallbackElement(hidden);
var table = app.createFlexTable().setId("myTable");
var button = app.createButton("+", handler);
app.add(listBox).add(button).add(table).add(hidden);// add all widgets to the app
return app;
}
function buttonHandler(e) {
var app = UiApp.getActiveApplication();
var pos = e.parameter.hidden;// get the position (is a string)
if(pos==null){pos='0'};// initial condition, hidden widget is empty
pos=Number(pos);// convert to number
var table = app.getElementById("myTable")
table.insertRow(pos).insertCell( pos, 0).setText(pos, 0, e.parameter.myListBox);// add the new item at the right place
++pos ;// increment position
app.getElementById('hidden').setValue(pos);// save value
return app;// update app
}
The following piece of code is doing the same, but with the usage of a ScriptProperties method. Nothing fancy to it and works like a charm:
function doGet() {
var app = UiApp.createApplication();
// create listBox and add items
var listBox = app.createListBox().setName("myListBox")
.addItem("item 1")
.addItem("item 2")
.addItem("item 3");
// set position
ScriptProperties.setProperty('position', '0');
// create handle for button
var handler = app.createServerHandler("buttonHandler").addCallbackElement(listBox);
// create flexTable and button
var table = app.createFlexTable().setId("myTable");
var button = app.createButton("+", handler);
// add all to application
app.add(listBox)
.add(button)
.add(table);
// return to application
return app;
}
function buttonHandler(e) {
var app = UiApp.getActiveApplication();
// get position
var position = parseInt(ScriptProperties.getProperty('position'),10);
// get myTable and add
app.getElementById("myTable").insertRow(position).insertCell(position, 0).setText(position, 0, e.parameter.myListBox);
// increment position
var newPosition = position++;
// set position
ScriptProperties.setProperty('position', newPosition.toString());
// return to application
return app;
}
Please de-bug the code, so that the authorization process is initiated.
Reference: Script and User Properties
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;
}