Google SpreadSheet saving "undefined" values - google-apps-script

My case is this, I'm Trying to save data or values taken from a form created on GUI Builder, the first time I've used the example script posted on [link to google script documentation ][1]
It worked good, I was able to take values typed at the form, but when I decided to use the GUI Builder because, we need more text fields and stuff, It stop working, Every time that I click the button, my spreadsheet gets an "Undefined" value.
I've checked all the IDs and all of them are exactly as the script.
The code:
_guardar : This function inserts textfield values to the spreadsheet.
celda means cell in english
ultimaFila means lastRow
function doGet(e) {
var doc = SpreadsheetApp.openById('0Au-gVDPWE5-bdDhJQzc2N2U4Q2l3LVY2cWpkbHdXMVE');
var app = UiApp.createApplication();
app.add(app.loadComponent("MyForm"));
return app;
}
function _guardar(e){
var app = UiApp.getActiveApplication();
var doc = SpreadsheetApp.openById('0Au-gVDPWE5-bdDhJQzc2N2U4Q2l3LVY2cWpkbHdXMVE');
var ultimaFila = doc.getLastRow();
var celda = doc.getRange('a1').offset(ultimaFila, 0);
celda.setValue(e.parameter.nombre);
celda.offset(0, 1).setValue(e.parameter.empresa);
celda.offset(0, 2).setValue(e.parameter.nit);
celda.offset(0, 3).setValue(e.parameter.sector);
celda.offset(0, 4).setValue(e.parameter.telefono);
celda.offset(0, 5).setValue(e.parameter.email);
celda.offset(0, 6).setValue(e.parameter.web);
celda.offset(0, 7).setValue(e.parameter.descripcion);
app.close();
return app;
}
This is similar to the example code at the google documentation site.![enter image description here][2]
Also I've checked that the "OnClick" event ,is it set on "_enviar" function.
All the textboxs and labels are contained in a Absolute panel, including the buttons.

It is not the IDs that have to correspond to the parameter but the names.
These are defined in another tab in the GUI builder and are used to get the values with e.parameter.name
see screen capture below :
EDIT : following your comment, did you check the callback element in the GUI ? is it the parent panel ?

Related

Run server-side code with google chart select

I have a google visualization table that I'm publishing in a web app.
Background:
I run a script that lists all the documents in a google folder in a spreadsheet. I then push that list into the google table I have published in the web app.
The need:
I want to manage those that same list of documents directly from the web app. I want to be able to to move the document from one folder to another when I select the applicable row on the table.
Two things I have accomplished:
I have a script that will move the document to a specific folder on
the google drive using it's doc id.
I have an event listener on the table so that when you click a row and
then click the delete icon, you get a prompt that asks, "are sure
you want to archive [enter document name]?" When I click ok, I get my
test prompt that says "document archived". When I click no, I get my
test prompt that says "request cancelled". So from this, I know I
have the appropriate code (at least that's how it seems).
What I'm struggling with:
I can't seem to get the codes above to work together. The event listener is providing me the url of the document which I have parsed to give me only the id. This is what I was hoping to use to get the rest of the code to run, but I think because I'm trying to interact with the server-side from the client-side, it's not working. Can anyone help me figure it out? I know that I need to use google.script.run.withSuccessHandler when running a server side script from the client side, but I don't know how it applies to this case the docid I need is being collected on table select. Any help is appreciated and I hope the above makes sense!
// Draw Dashboard
h2dashboard.bind([h2stringFilter, h2typeFilter], [h2chart]);
h2dashboard.draw(h2dataView);
google.visualization.events.addOneTimeListener(h2chart, 'ready', function() {
google.visualization.events.addListener(h2chart.getChart(), 'select', function() {
var selection = h2chart.getChart().getSelection();
var dt = h2chart.getDataTable();
// Get Value of clicked row
if (selection.length) {
var item = selection[0];
var docurl = dt.getValue(item.row, 1);
var docname = dt.getValue(item.row, 0);
var source = dt.getValue(item.row, 3);
// When button is clicked, show confirm box with value
$(document).ready(function() {
$("#hu2archive").on("click", function() {
var answer = confirm("Are you sure you want to archive " + docname + "?");
if (answer === true) {
var archive = DriveApp.getFolderById("FOLDER ID");
var docid = docurl.match(/[-\w]{25,}/); // This is where I'm grabbing the value from the row.
var doc = DriveApp.getFileById(docid);
doc.makeCopy(archive).setName(doc.getName());
source.removeFile(doc);
alert(docname + " has been archived!");
} else {
alert("Request cancelled");
}
});
});
}
});
});
I just got it! What I was having a hard time understanding was how to pass a variable from the client side to code.gs. I have only run a script in code.gs from the client side on button submit but never passed anything back.
So I ended up changing my code to the below which passes the variable I need into a successhandler where archiveDoc is the function in my code.gs and docurl is the name of the variable I need to pass from the eventlistener.
if (answer === true) { google.script.run.withSuccessHandler(onSuccess).withFailureHandler(err).archiveDoc(docurl);
I'm still new to coding so I just learned something new! So thanks Spencer Easton. I did in fact answer my own question.

Using server handlers with modal dialogs

I am displaying a User Interface over a sheet using showModalDialog passing in the app I just created. I also setup a button with a server handler. When server handler function is called I try to get the app again using "UiApp.getActiveApplication()" to hide some elements and show some different elements, however, the changes are not reflected. At the end of the method I tried to close the app, and show a new modal dialog, I tried to return the app, I tried to do nothing, and nothing seems to work.
I can't post my whole code since it is very long, so I made a very simple version that gets the point across. When I put some logging statements in testHandler() it proves that the code is running.
function test() {
var app = UiApp.createApplication().setHeight(700).setWidth(1500);
var label = app.createLabel("Hi").setId("label");
var label2 = app.createLabel("GoodBye").setId("label2").setVisible(false);
var button = app.createButton("Press Me").setId("button");
app.add(label);
app.add(label2);
app.add(button);
var testHandler = app.createServerHandler('testHandler');
testHandler.addCallbackElement(label);
testHandler.addCallbackElement(label2);
button.addClickHandler(testHandler);
SpreadsheetApp.getUi().showModalDialog(app, 'Test');
}
function testHandler() {
var app = UiApp.getActiveApplication();
app.getElementById('label').setVisible(false);
app.getElementById('label2').setVisible(true);
// Not sure what to do now
}
Thank you in advance for your help
return app; //where you are not sure what do do

GAS - define listBox() displayed value

I have a form built and displayed in UiApp which uses the listBox class. My listBoxes are created as in the example below:
var yearText = app.createListBox().setName("yearText")
.addItem("")
.addItem("Reception")
.addItem("Nursery");
When the form loads for the first time they default to display blank (the value at index 0). What I am trying to do is reload the form with stored data, populating the listBox with it's saved value whilst still offering the same options as above (blank, Reception, Nursery).
I have tried using the various setValue methods available to the listBox class but I am not making any progress (variations of setValue are working fine for textArea, checkBox and dateBox classes elsewhere in the form). Any help or guidance gratefully received!
The method for that is setItemSelected(index,Boolean), example below
function doGet(){
var app = UiApp.createApplication();
var list = app.createListBox().addItem('').addItem('v2').addItem('v3').addItem('v4').setItemSelected(2,true);
app.add(list);
return app;
}
Or also : setSelectedIndex(index) (same link for doc
example below too
function doGet(){
var app = UiApp.createApplication();
var list = app.createListBox().addItem('').addItem('v2').addItem('v3').addItem('v4').setSelectedIndex(2);
app.add(list);
return app;
}

Multiple Page UI using UiService

I would like to use Google Apps Script UiService to produce a multiple page user interface.
Here's what I've got so far:
function doGet(e)
{
var app=UiApp.createApplication();
var nameLabel=app.createLabel('Name:');
var button=app.createButton("next");//my button on clicking,trying to divert to other UI
var handler=app.createServerHandler("myclick");
button.addClickHandler(handler);
app.add(namelabel);
app.add(button);
return app;
}
function myClick(){
//on clicking the button it should call the other ui or other html page
is there any method for that.}
How can I do this?
You should look at How To Allow Users to Review Answers before Submiting Form?, which has an example that does this.
The idea is to create your UiApp with multiple Panels, then show or hide them in response to user actions, using setVisible(). (If you were using the HtmlService, you would enclose your "pages" in different <div>s, and change their display attributes. See toggle show/hide div with button?.)
The Best Practices also describes use of client-side handlers for responsiveness, so let's try that.
/**
* Very simple multiple page UiApp.
*
* This function defines two panels, which appear to the end user
* as separate web pages. Visibility of each panel is set to
* control what the user sees.
*/
function doGet() {
var app = UiApp.createApplication();
var page1 = app.createFlowPanel().setId('page1');
var page2 = app.createFlowPanel().setId('page2');
// Content for Page 1
page1.add(app.createLabel('Page 1'));
var page1Button = app.createButton('Next Page');
page1.add(page1Button);
// Create client handler to "change pages" in browser
var gotoPage2 = app.createClientHandler()
.forTargets(page1).setVisible(false)
.forTargets(page2).setVisible(true);
page1Button.addClickHandler(gotoPage2);
// Content for Page 2
page2.add(app.createLabel('Page 2'));
var page2Button = app.createButton('Previous Page');
page2.add(page2Button);
// Create client handler to "change pages" in browser
var gotoPage1 = app.createClientHandler()
.forTargets(page1).setVisible(true)
.forTargets(page2).setVisible(false);
page2Button.addClickHandler(gotoPage1);
app.add(page1);
app.add(page2);
// Set initial visibility
page1.setVisible(true);
page2.setVisible(false);
return app;
}
That works for changing the view of the UI. To extend this for general purposes, you would likely want to add server-side handlers to the same buttons to perform work, and update the contents of the panels as things progress.
Here is working code
that demonstrates a multiple page form, i.e. it does the initial doGet() and then lets you advance back and forth doing multiple doPost()'s. All this is done in a single getForm() function called by both the standard doGet() and the doPost() functions.
// Muliple page form using Google Apps Script
function doGet(eventInfo) {return GUI(eventInfo)};
function doPost(eventInfo) {return GUI(eventInfo)};
function GUI (eventInfo) {
var n = (eventInfo.parameter.state == void(0) ? 0 : parseInt(eventInfo.parameter.state));
var ui = ((n == 0)? UiApp.createApplication() : UiApp.getActiveApplication());
var Form;
switch(n){
case 0: {
Form = getForm(eventInfo,n); // Use identical forms for demo purpose only
} break;
case 1: {
Form = getForm(eventInfo,n); // In reality, each form would differ but...
} break;
default: {
Form = getForm(eventInfo,n) // each form must abide by (implement) the hidden state variable
} break;
}
return ui.add(Form);
};
function getForm(eventInfo,n) {
var ui = UiApp.getActiveApplication();
// Increment the ID stored in a hidden text-box
var state = ui.createTextBox().setId('state').setName('state').setValue(1+n).setVisible(true).setEnabled(false);
var H1 = ui.createHTML("<H1>Form "+n+"</H1>");
var H2 = ui.createHTML(
"<h2>"+(eventInfo.parameter.formId==void(0)?"":"Created by submission of form "+eventInfo.parameter.formId)+"</h2>");
// Add three submit buttons to go forward, backward and to validate the form
var Next = ui.createSubmitButton("Next").setEnabled(true).setVisible(true);
var Back = ui.createSubmitButton("Back").setEnabled(n>1).setVisible(true);
var Validate = ui.createSubmitButton("Validate").setEnabled(n>0).setVisible(true);
var Buttons = ui.createHorizontalPanel().add(Back).add(Validate).add(Next);
var Body = ui.createVerticalPanel().add(H1).add(H2).add(state).add(Buttons).add(getParameters(eventInfo));
var Form = ui.createFormPanel().setId((n>0?'doPost[':'doGet[')+n+']').add(Body);
// Add client handlers using setText() to adjust state prior to form submission
// NB: Use of the .setValue(val) and .setValue(val,bool) methods give runtime errors!
var onClickValidateHandler = ui.createClientHandler().forTargets(state).setText(''+(parseInt(n)));
var onClickBackHandler = ui.createClientHandler().forTargets(state).setText(''+(parseInt(n)-1));
Validate.addClickHandler(onClickValidateHandler);
Back.addClickHandler(onClickBackHandler);
// Add a client handler executed prior to form submission
var onFormSubmit = ui.createClientHandler()
.forTargets(state).setEnabled(true) // Enable so value gets included in post parameters
.forTargets(Body).setStyleAttribute("backgroundColor","#EEE");
Form.addSubmitHandler(onFormSubmit);
return Form;
}
function getParameters(eventInfo) {
var ui = UiApp.getActiveApplication();
var panel = ui.createVerticalPanel().add(ui.createLabel("Parameters: "));
for( p in eventInfo.parameter)
panel.add(ui.createLabel(" - " + p + " = " + eventInfo.parameter[p]));
return panel;
}
The code uses a single "hidden" state (here visualized in a TextBox) and multiple SubmitButton's to allow the user to advance forward and backward through the form sequence, as well as to validate the contents of the form. The two extra SubmitButton's are "rewired" using ClientHandler's that simply modify the hidden state prior to form submission.
Notes
Note the use of the .setText(value) method in the client handler's. Using the Chrome browser I get weird runtime errors if I switch to either of the TextBox's .setValue(value) or .setValue(value, fireEvents) methods.
I tried (unsuccessfully) to implement this logic using a Script Property instead of the hidden TextBox. Instead of client handlers, this requires using server handlers. The behavior is erratic, suggesting to me that the asynchronous server-side events are occurring after the form submission event.
You could load different UI's on reading the parameters in your app.
The doGet(e) passes the parameters in the app's url. This way you could call your app with for example: ?myapp=1 (url parameter).
in your doGet you could read that parameter with: e.parameter.myapp
This way you could load different applications depending on the parameters that where passed.
You could just change your button with a link (to your own app, with different url parameters).
You could also do it with buttons and handlers but the above way has my preference.
If you want to use a button<>handler just change you main (first panel) and each time add a completely new panel to your app object. This way you would start from scratch (i.e. create a new application).

GUI apps script, returning "undefined " value

i've created simple GUI with a flow panel name and ID main panel, a label name Label1 a textbox name myTextBox and a button with ID getETA.
my aim is if i enter a value in text box and click submit den the value should write in spreadsheet.
my problem is the script is returning undefined in spreadsheet not the actual value i've entered.
var app = UiApp.createApplication();
app.setTitle("My Application");
app.add(app.loadComponent("MyGui"));
SpreadsheetApp.getActiveSpreadsheet().show(app);
var clickHandler = app.createServerHandler('clickGetETA');
clickHandler.addCallbackElement(app.getElementById('mainPanel'));
app.getElementById('getETA').addClickHandler(clickHandler);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
// this function responds to submit button
function clickGetETA(e) {
var app = UiApp.getActiveApplication();
var textBoxValue = e.parameter.myTextBox;
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow()+1;
var lastCell = sheet.getRange("A"+lastRow);
lastCell.setValue(textBoxValue);
return app.close();
}
I'm new to stackoverflow dont have enough reputations to post image so posting links of images
image1
image2
image3
image4
image5
image6
image7
I see that you defined the clickHandler in the script AND in the GUI builder... that is one too much, that's probably the cause of the issue
Please try to remove the one in the GUI builder and test again, I don't see any other problem in your test ;-)
You could also of course remove the handler in the script but in that case you should add the callbackelement in there too by developing the smal '+' near the handler name and put your panel there.
EDIT : Sorry, there are 2 handlers in the GUI builder, so that is two too much ! you cannot use a single handler with a single name on 2 different handler type (click & key) if you want to have multiple handlers on a button you should define as many handlers you need, each of the type you want, eventually calling the same function but with different variable names.
Edit 2 the line SpreadsheetApp.getActiveSpreadsheet().show(app); in the begining of the main function souldn't be there either, just remove it since you call the spreadsheet later with var doc = SpreadsheetApp.getActive();
Working code with GUI here
EDIT 3 : issue solved by sharing the questioner sheet, the panel name was not correctly written : mainPanel in the script and mainpanel in the GUI ...
Aaaah, case sensitiveness ! ;-) (visible in image6)