I’m working on a Google Apps Script project bound to a Spreadsheet. For a certain functionality, I display a modal dialog as follows: Modal dialog. So, a series of selectors, variable in number, that are created and loaded dynamically depending on some data. Each selector is contained within a <li> list element of a <menu> unordered list: <menu id="categories"></menu>
.
Upon clicking on the ‘Ok’ button, a certain function is called; here is the HTML element:
<!-- An 'Ok' button to continue. -->
<input type="button" value="Ok" onclick="endDialog()" />
Now, this function endDialog extracts the data from the selectors, and builds a Map<number, number> object and an array, which in turn are passed as arguments to a server-side Apps Script function.
And here comes the problem: the endDialog function does everything correctly until the google.script.run.applyColouring(rowPairs, colourAvailabilities);
, at which point I get the uncaught error described in the title: Error
Here are some insights:
- The problem lies within the Map object part (rowPairs), since when only using the array argument (colourAvailabilities) in the server-side function, everything works nicely.
- The server-side function is never called: it never shows in the Apps Script ‘Executions’ tab. Moreover, whenever I run this server-side function on the Apps Script side directly, with the exact same parameters, it works as expected.
- The Map object is correctly built, or at least that is what I deduce when I log it into the console: Map object
- When I modify the code to use an Object instead of a Map, it suddenly works. So I can make it work, but at this point I’m just curious and I want to know what on earth is going on. Besides, a Map – instead of a plain Object – is exactly what I need, since (among others) I am dealing with numbers.
- I wonder if the problem is the impossibility of passing a Map as a parameter to the server-side function, but the documentation doesn’t seem to suggest that:
Legal parameters and return values are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects and arrays. A form element within the page is also legal as a parameter, but it must be the function’s only parameter, and it is not legal as a return value. Requests fail if you attempt to pass a Date, Function, DOM element besides a form, or other prohibited type, including prohibited types inside objects or arrays. Objects that create circular references will also fail, and undefined fields within arrays become null.
- I’m not sure anymore, but I would swear that everything worked fine two days ago when I tested the finalised thing. I’m not sure what I could have modified since then.
Here is the whole HTML-side function endDialog:
/**
* Calls a server-side function to apply the changes
* to the categories' colours and closes the dialog.
*
* @return {void} Nothing.
*/
function endDialog() {
// Hide the error messages.
document.getElementById("emptyColour").style = "display:none;";
document.getElementById("repeatedColour").style = "display:none;";
// Get the list of categories.
const categories = Array.from(document.getElementById("categories").children);
// Create a map to contain the list of category-colour pairs.
const rowPairs = new Map();
// Create an array to contain the column of boolean values
// corresponding to the availabilities of each colour palette.
// Initialise all colours as being available.
const colourAvailabilities = Array.from({length:10}).map(() => [true]);
// A variable to see if all colours have been selected properly.
var validSelects = true;
// A variable to see if all colours are different from each other.
var uniqueSelects = true;
// Initialise an index variable to go through the categories.
var i=0;
// While the colours are validly selected,
// and unique, go through the categories.
while (validSelects && uniqueSelects && i<categories.length) {
// Get the corresponding row number,
let catRow = Number(categories[i].value);
// and its chosen colour's row number.
let colourRow = Number(document.getElementById(i+1)
.value
.split(',')[0]);
// Check if the selected option is a valid colour.
validSelects = colourRow != 0;
// If the selected option is a valid colour,
if (validSelects) {
// set the corresponding colour to being unavailable.
colourAvailabilities[colourRow-6][0] = false;
// Add the rows pair to the map.
// Use the colour row number as the key, so as to easily check whether the
// number of categories and the number of key-value pairs in the map are equal.
// Indeed, if this colour has already been used, no new key-value pair will
// be added, instead just updating the value associated to the colour's row key.
rowPairs.set(colourRow, catRow);
// Check if all selected colours are different from each other.
uniqueSelects = rowPairs.size == i+1;
}
// Go to the next category.
i++;
}
// If the user has made a valid and unique colour selection,
if (validSelects && uniqueSelects) {
console.log(rowPairs, colourAvailabilities);
// call a server-side function to apply the changes.
google.script.run.applyColouring(rowPairs, colourAvailabilities);
// Close the dialog.
google.script.host.close();
}
// If one of the selected colours is invalid,
else if (!validSelects) {
// display an error message.
document.getElementById("emptyColour").style = "display:block;";
}
// If some colour has been selected more than once,
else {
// display an error message.
document.getElementById("repeatedColour").style = "display:block;";
}
}
I don’t believe my sharing of the rest of the code is necessary, but if you want/need either the server-side function code, or the whole HTML file, I can post them.
If anyone knows what the problem is, I’m all ears! Thanks in advance.