Problems using the Apps Script GUI builder - google-apps-script

So I'm currently working on the gui of an item checkout system that runs within a spreadsheet. It does everything it's supposed to and isn't returning anything null, usually I'd be really happy with how it's turning out.
However, I keep getting "error encountered, an unexpected error occured" whenever I press the submit button, after that the rest of the code runs as expected. Every once and a while I'll get no error, but the vast majority of the time it throws the error.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [{name: "Equipment Checkout", functionName: "testing"}];
ss.addMenu("Equipment Checkout", menuEntries);
}
function testing(){
var app = UiApp.createApplication();
app.add(app.loadComponent("ImprovedCheckout"));
var doc = SpreadsheetApp.getActiveSpreadsheet();
doc.show(app);
}
function submitted(e){
Browser.msgBox(e.parameter.requestor);
}
This is the simplest version of my code.
I'm using the gui builder to create the gui and I'm also using it to make the event handler. I've been banging my head against the wall for the past hour because it seems like the error is happening before the first line of submitted(e) but it doesn't occur until I actually click the submit button. It doesn't happen every time but it does happen the majority of the time.
I imagine I'm handling events from the builder wrong but there is oh so little information on that feature yet.

I've seen this error happen when people set two handlers for the same button. Normally one via the GUI editor and another on the code. One works and the other fails. Which gives this crazy impression that it throws an error and yet works.
But since you said this is the exact code you're using. Are you setting multiple handlers on the GUI builder? I don't know even if that's possible, I never use the GUI builder.

FIGURED IT OUT!
Instead of using a submit button, I changed it to just a regular old button. It looks like the submit button was trying to run something else at the same time.
It looks like submit buttons have some additional rules and requirements but for the time being, I'm just using a normal button.

I have had no issues running the code you have supplied. Two things to mention that are key:
Make sure that you're adding a callback element to the ServerHandler with the HTML name of "requestor" as you are using in code.
When you add the callback element, use the ID in the GUI editor. Name != ID
Both Name and ID need to be set in this example.

I have got this same error message when using getElementbyId('id") and having a wrong value as ID. The script itself don't really stop and everything might seem normal apart from the action this specific call is supposed to do (but this is not always 'obvious')

Related

remaking code from in the same function doesn't work

On the client side I have a form that is submitted to a 'processFrom' function on the google app script server side when submitted. However the code got long and spaghetti like so I decided to just scratch it and start over. Essentially erased everything between the curly brackets. Now nothing will run inside the processForm function even just a simple Logger.log('in the function'); Won't log. I'm pretty baffled.
the client side button code that works when the old code is there:
<input type="button" class="btn" value="Adding to list" onclick="google.script.run.processForm(this.parentNode);formSubmitted();">
This is the server side that doesn't seem to process:
function processForm(formdata){
Logger.log(formdata);
Logger.log('in the loop');
}
Again I'd like to reiterate that all I did is delete spaghetti code from inside the curly brackets for processForm and all of the sudden it doesn't work. If I copy paste the spaghetti code back it miraculously works. By work I mean any code inside processForm starts to run and logg etc.
Update:
I went through the code in the functions processFrom and deleted line by line and check if the function is still working. The code is -
function processForm(formdata){
Logger.log(formdata);
var cSpreadsheet = DriveApp.getFilesByName(formdata.Name);
var spreadsheet = getListing();
}
This logs about one out of every four submits. If I take out the two variables I can't get formdata to log at all.
Update 2:
After more review I think I found the problem. The line is the problem (more specifically the formSubmitted() function):
onclick="google.script.run.processForm(this.parentNode);formSubmitted();"
In formSubmitted I have another call to google.script.run for something else, which overrides the google logs with nothing since in that function I do not have anything logging. The log only came up once in a while for processForm because I must have switched fast enough to the other tab and looked at the logs quickly before the formSubmitted function completed or ran.
After more review I think I found the problem. The line is the problem (more specifically the formSubmitted() function):
onclick="google.script.run.processForm(this.parentNode);formSubmitted();"
In formSubmitted I have another call to google.script.run for something else, which overrides the google logs with nothing since in that function I do not have anything logging. The log only came up once in a while for processForm because I must have switched fast enough to the other tab and looked at the logs quickly before the formSubmitted function completed or ran.

Basic UiApp failing with error "ReferenceError: "UIApp" is not defined."

I am creating some google app scripts for my company to use to generate random tests for employee training. I already have the basic scripts written to grab a list of questions from a google sheet, randomize them, grab the first 10 questions, etc. That all works fine. I decided it might be better to re-do the whole thing using a UiApp instead of just separate scripts. That is where the problem comes in. I did a simple bare bones UiApp to test with, published it and tried to hit the URL and that's where I encounter this error. I searched for this error and all I could find was some discussion about this being part of google apps premiere(which should have been folded into regular google apps around 2010). I've been staring at this so long I've frustrated myself. It should be something very simple and yet it's refusing to work. I'm assuming I am doing something wrong at a basic level but I've reached the point where my brain refuses to see it.
Here is the basic script I started with:
function doGet(e) {
var app = UiApp.createApplication();
var mainPanel = app.createVerticalPanel().setId('mainPanel');
mainPanel.add(app.createLabel('test'));
return app;
}
I save it, publish it and go to the URL and that's when I get the above error message. I know it's something simple but I've reached the point of frustration and simply can't see it.
Update: to reflect comments
Another possibility for WebApps not updating is not publishing a new version and only checking the exec URL. For instant changes to the code, always check the dev URL. The exec will only change after saving a version in Manage Versions and re-publishing the app.
First Answer:
I think your question title says it all.
UIApp is not defined, but Class is UiApp. JS is case sensitive. I copied and pasted the code exactly as it is in your question and received no errors. I did have to add one line to make the label show up.
function doGet(e) {
var app = UiApp.createApplication();
var mainPanel = app.createVerticalPanel().setId('mainPanel');
mainPanel.add(app.createLabel('test'));
app.add(mainPanel); // <-- I added this line to see the label
return app;
}

New Google Sheets custom functions sometimes display "Loading..." indefinitely

SPECIFIC FOR: "NEW" google sheets only.
This is a known issue as highlighted by google in the new sheets.
Issues: If you write complex* custom functions in google-apps-script for google sheets, you will occasionally run into cells which display a red error box around the cell with the text "Loading..."
Google has suggested:
If this occurs, try reloading the page or renaming the function and changing all references to the new name.
However for other developers experiencing this issue (and who are unable to escape the "loading..." error), I've written my findings in the answer below on how to get past this (with limitations) consistently.
*We're treating this question as the canonical answer for Google Sheet's indefinite "Error... Loading data" problem. It's not limited to complex or slow functions.
Important Tip: Create multiple copies of your entire spreadsheet as you experiment. I have had 3 google spreadsheets corrupted and rendered completely in-accessible (stuck in a refresh loop). This has happened when I was experimenting with custom functions so YOU HAVE BEEN WARNED!
You will want to try one or many of the following ways to fix this issue:
As suggested by google, try re-loading the spreadsheet or re-naming the function or changing the parameters in the cell to see if this fixes the issue.
Surround ALL your custom functions in a try-catch block. This will help detect code issues you may not have tested properly. Eg:
try{
//methods
}catch(ex){
return "Exception:"+ex;
}
Revert to the old sheets and test your functions and check for any other type of error such as an infinite loop or invalid data format. If the function does not work in the old sheets, it will not work in the new sheets and it will be more difficult to debug.
Ensure NONE of your parameters refer to, can expect to or will ever contain a number larger than 1 million (1000000). No idea why but using a number larger than a million as any parameter will cause your function to fail to execute. If you have to, ask the input to be reduced in size (maybe divide by 1000 or ask for M instead of mm).
Check for numeric or floating point issues where numbers may exceed a normal set of significant figures. The new sheets seems to be a little glitchy with numbers so if you are expecting very large or very complex numbers, your functions may not work.
Finally, if none of the above work, switch to the old google sheets and continue working.
If you find any other limitations or causes for functions to fail to execute, please write them below for me and other users who are heavy g-sheet users!
I also had the infinite loading issue with the following function.
// check if an item can be checked off
function checkedOff( need, have ) {
var retStr = "nope";
if( have >= need ){
retStr = "yep";
}
return retStr;
};
Turns out you shouldn't have a trailing ";". Removing the semicolon solved the problem.
// check if an item can be checked off
function checkedOff( need, have ) {
var retStr = "nope";
if( have >= need ){
retStr = "yep";
}
return retStr;
}
This runs as one would expect.
FWIW, I just ran into this and the culprit ended up being a getRange() call that pulled several thousand rows into an array. Periodically it would get hung on the "Loading..." message.
I worked around it by putting that range into the document cache. It's a little kludgy because the cache only stores strings, not arrays, but you can force it back into an array using .split(',') when you need to access the array.
(In my case it's a single array. There's probably a way to do it using a double array, either by sending each row or column into its own cache, or reading the cache value back N items at a time, each N becoming its own array.)
Here's the relevant bit from my code:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("mySheet"); //search the "mySheet" sheet
// is the big list already in the cache?
var cache = CacheService.getDocumentCache();
var cached = cache.get("columnValues");
if (cached != null) {
var columnValues = cached.split(','); // take the cached string and make it an array
} else { // it's not in the cache, so put it there
var column = 1; // the column with your index
var columnValues = sheet.getRange(2, column, sheet.getLastRow()).getValues(); // first row is header
cache.put("columnValues",columnValues,60000); // this forces the array into a string as if you used .join() on it
}
This is definitely a bug in Apps Script -- getRange() shouldn't hang without a timeout or error message. But at least there's a workaround. Here's the bug I opened against it, where I've also put the full code.gs from my sheet.
One cause: Permissions needing authorizing.
As far as {this problem, better phrased the cell result(s) of a custom function displaying the disgustingly-vague message ‘Loading... Error: loading data...’}, indeed in the case where all instances of the same/similar custom function call displaying this error, is that Google Sheets needs permissions to run the script (often additionally: meaning in the past it didn't need these), so instead of {acting appropriately: then prompting the user for these permissions else returning that error}, Sheets instead hangs with this disgustingly vague error.
Additional permissions can be needed from 1 or more:
Google App Scripts has since rewriting their permission structure --how this problem now just happened to me, per my internal note O80U3Z.
Your code or some library it uses made changes to require more access ...but in this case you have a much better chance of guessing the cause of this disgustingly-vague error, so hopefully won't be reading here.
To fix, I explicitly ran my GAS spreadsheet code by both: clicking one of my custom menu functions and, in the ‘script editor’, running one of my custom JS functions notably the ‘onOpen()’ since that is most comprehensive. The first promoted me for indeed new permissions, via popup ‘Authorization RequiredThe application "MM6ZBT(MM6Z83 script)" needs authorization to run.’, though onOpen() also did this in cases of GAS revising its permissions since we used that sheet. Then, as I was still getting this ‘Loading...’ error, I reloaded the web page (so the sheet), and, at least for these cases of this disgustingly vague error, it was gone and the computations worked fine :-)
TL;DR - Try duplicating the sheet tab and delete the old one
I struggled with this issue today, and tried some of the approaches mentioned. For various reasons, renaming the function wasn't possible for me.
It was very clear that if I called a my function like this in cell X25:
=myFunction("a", 1, "b", 2, "c", 3)
The cell would be stuck "Loading...", while just changing a parameter slightly (e.g. converting a number to a string) the cell would evaluate fine.
=myFunction("a", "" & 1, "b", 2, "c", 3)
Just copying the code into another cell (e.g. X24) and executing it there seemed to bypass the problem. As soon as I moved it back to the original parameters or cell, it got stuck "Loading..." again.
So I would assume it's some kind of caching of "Cell ID", function and parameters that go bonkers on Google's side.
My good-enough solution was to simply duplicate the Sheet tab, delete the old one, and finally rename the new one back to the original name. This solved the problem for me.
I also had the "loading data..." error but none of the fixes described here worked for me. It didn't seem to be caused by the issues described here. In my case, I narrowed it down to a specific floating point operation issue (it looks like a real bug in Google Sheets to me), and documented one possible work around at
Google Sheets / Apps "Loading data" error: is there a better workaround?
To summarize (at the request of commenter Steve), if a cell with
= myfunction(B10)
generated a "loading data" error, then for me it could be fixed by wrapping the argument in a "value()" function:
= myfunction(value(B10))
which converts the number in cell B10 (which seemed like a normal number but generated problems somehow) into a normal number that works fine.
I also had the problem that you explained. It seems that it can be caused in more than one way.
I ended up finding that my custom function was displaying that error because it relied on data from an =IMPORTRANGE() call, and that call was failing.
I eventually found that the =IMPORTRANGE() call was failing because I had forgotten to update the URL that it was importing from when I had uploaded a new version of that imported-from sheet. It seems that trying to IMPORTRANGE from a trashed file can cause the infinite "Loading..." error.
Update 2022
It looks like this bug is still happening. I tried ALL the solutions mentioned here but none worked.
What worked was to start with a blank slate. I recreated the file, copy-pasted my data, reapplied my preferred style and format, and lo-and-behold the sheet finally managed to pull the data using my custom functions.
This is a definitely a bug on Google's end - and it's all the more annoying because they removed the "Report a problem" button from the "Help" section.
Nevermind
The newer sheet has stopped working too. This is so annoying ..
The problem is that when a custom function formula cell starts showing Loading..., the custom function does not get called at all. The code in the script project thus does not come into play. Even the simplest custom functions sometimes suffer from the issue.
The problem usually goes away if you clear the formula cell and undo, or slightly edit the custom function's parameters to cause it to get re-evaluated. But that does not solve the issue. Google has been dragging their feet solving the underlying cause for many years.
To help the issue get Google's attention, star issue 233124478 in the issue tracker. Click the star icon ☆ in the top left-hand corner to vote for fixing the issue and get notified of status changes. Please do not post a "me too" or "+1" reply, but just click the star icon. Google prioritizes issues with the most stars.
Add-ons
I had two add-ons, and no function was loading.
I removed them, and all is well!
For me, renaming the custom function solved the problem. For now at least.
Just to add to Azmo 's answer...
I in fact removed all trailing semi-colons from the code:
// check if an item can be checked off
function checkedOff( need, have ) {
var retStr = "nope"
if( have >= need ){
retStr = "yep"
}
return retStr
}
And discovered, that when doing this over a large range you can also max out the acceptable number of calls to the API.
To get around it I added an IF THEN check around my custom script call.
So instead of:
=checkedOff(H10,H11)
Use something like this to check for a populated field before execution:
=if(H17<>"-",checkedOff(H10,H11),0)
My app script pulling data from my MSSQL database displayed just fine on GoogleSheets my laptop browser but then did not display on the Android GS app.
Per this thread it looks like there's a number of issues that could cause this, but #DestinyArchitect's answer above re: Permissions seemed like the simplest fix.
While testing my app script, Sharing was off for this GoogleSheet file.
Once I moved it to my team's folder where we have default Sharing switched on with a few team members, the MSSQL data showed right up on the GoogleSheet in my Android GS app.
Easy fix, this time...
In my case, the cell was stuck with a Loading... message due to "probably" a race condition between functions and formulas resolutions.
This is my custom function:
function ifBlank(value1, value2) {
return !!value1 ? value1 : value2;
}
This is the formula calling it:
=IFBLANK(VLOOKUP($A2,Overrides!$A$2:$E,5,FALSE),VLOOKUP($A2,'_resourceReq'!$A$2:$C,3))
Those VLOOKUP values could be pretty complex and could also take some time to resolve.
Solution: (in my case)
Wrapping the VLOOKUP() into TO_TEXT() or VALUE() for example.
So, changing the formula to =IFBLANK(TO_TEXT(VLOOKUP($A2,Overrides!$A$2:$E,5,FALSE)),TO_TEXT(VLOOKUP($A2,'_resourceReq'!$A$2:$C,3))) worked well.
If that doesn't work maybe try resolving the value from a function into a cell before using it as the argument of your custom function.
In my case, multiple cells using functions experienced this issue, but the simple answer was... wait.
In my case, I was scraping data via importXML functions across multiple rows and
columns. I was thrilled with the results, feeling on top of the world, then "Loading..." started showing its ugly face. For way too long. That's how I wound up here in troubleshooting mode, impatient and upset that Google was doing me wrong.
I tried many of the solutions here, only to find my "Loading..." antagonist acting unpredictably, popping up randomly like whacamoles, nothing to do with the code itself.
So. In my case, it was a matter of waiting it out (towards an hour for some rows, but I had so many cells fetching url data).
My layman's guess is that fetching data like this gets put in their bandwidth pipeline, lesser priority than typing a url into a search bar or other user requests.

Click Handler has suddenly stopped working

I have a script that is both published and been working for months that has suddenly stopped being able to find click handlers (no change to the code).
IE. I get an error like this:
Error encountered: Script function not found: interfaceClass.myClickHandler
"interfaceClass.myClickHandler" does, in fact, exist and it is registered
var indF = app.createCheckBox("Create individual files")
.setName('create')
.setId('createBox');
var chandler = app.createServerHandler('interfaceClass.myClickHandler');
indF.addClickHandler(chandler);
It also doesn't seem to be anything specific to this handler. ALL click handlers are failing.
Given this was working, I'm almost positive this is a result of Google doing an update that broke our code (which they've done in the past). But I need to know what they changed so I can work around it. Does anyone know?
So apparently what they broke is that app.createServerHandler can no longer find functions defined like so:
var obj = {};
obj.method = function (e){
};
You must now use functions declared the following way:
function method(e){
}

Apps Script GUI and createClientHandler

I am trying to use a client handler to validate an entry to make sure that the entry is indeed a number. So if an entry contains a letter, for example, then I want to disable a button so that a user cannot proceed. I have built a GUI using the GUI builder, but the client handler, specifically validateNotNumber, does not work if called from the GUI.
So far for a text box I am trying to get the validator to work in, I have in the GUI builder with the ID (Base) = Name (Input Fields), for the Events section I have the "on Key Press" filled in with the name of the function that has the client handler and the name of the form the text box resides on (in my case, AbsolutePanel1).
The function called is:
function isitnotnumbers(e) {
var app = UiApp.getActiveApplication();
app.createClientHandler().validateNotNumber(app.getElementById('ThisOdometer'))
.forTargets(app.getElementById('ButtonCalcMPG')).setEnabled(false);
return app;
};
The function is indeed called I have tested it by having a message displayed when a key is press in the text box. But the button specified is not disabled as it should when a non-number key is pressed.
The examples I found listed the validators in hand coded UI widgets, so I am not sure where I am going wrong when using the GUI builder.
So, where did I go wrong and how can I fix this?
Is there a work around if the GUI builder does have limitations with client handlers (such as using the HTML service)?
My other attempt(s):
I have tried to add a click handler to the text box, which is a modification to the code above:
var app = UiApp.getActiveApplication();
var handlernumbers = app.createClientHandler()
.forTargets(app.getElementById('LabelUserMessage'))
.setText('THe message');
var odobox = app.getElementById('ThisOdometer');
odobox.addClickHandler(handlernumbers);
return app;
Try this:
function isitnotnumbers(e) {
var app = UiApp.getActiveApplication();
app.getElementById('ThisOdometer').addKeyUpHandler(app.createClientHandler().validateNotNumber(app.getElementById('ThisOdometer'))
.forTargets(app.getElementById('ButtonCalcMPG')).setEnabled(false));
return app;
};
Edit:
You don't need this code in a separate function, just add the following line to your doGet() or in the main function (so that the handler is created only once when the app launches):
app.getElementById('ThisOdometer').addKeyUpHandler(app.createClientHandler().validateNotNumber(app.getElementById('ThisOdometer'))
.forTargets(app.getElementById('ButtonCalcMPG')).setEnabled(false));
To answer my own question, I will say that it does not matter as google is deprecating gui builder, and will cease to function after September.
I should, or perhaps I should have started to, learn uiservice or htmlservice.