I'm writing a web app that displays a subset of rows from a spreadsheet worksheet. For the convenience of users, the data is presented in a grid, each row selected from the spreadsheet forms a row in the grid. The number of rows relevant to each user grows over time.
The header of the grid is a set of buttons, which allow the user to control the sort order of the data. So if the user clicks the button in the header of the first column, the array that populates the grid is sorted by the first column and so forth. If you click the same column button twice, the sort order reverses.
I used script properties to communicate the selected sort field and order between the handler responding to the button presses, and the order function called by the sort.
So in doGet():
// Sort Field and Order
ScriptProperties.setProperties({"sortField": "date","sortOrder": "asc"});
And in the button handler:
function sortHandler(event) {
var id = event.parameter.source;
if (id === ScriptProperties.getProperty("sortField")) {
ScriptProperties.setProperty("sortOrder", (ScriptProperties.getProperty("sortOrder") === "asc")?"desc":"asc");
} else {
ScriptProperties.setProperties({"sortField": id, "sortOrder": "asc"});
}
var app = UiApp.getActiveApplication();
app.remove(app.getElementById("ScrollPanel"));
createForm_(app);
return app;
}
And the order function itself (this is invoked by a sort method on the array: array.sort(order);
in the code that defines the grid):
function orderFunction(a, b) {
var sortParameter = ScriptProperties.getProperties();
var asc = sortParameter.sortOrder === "asc";
switch(sortParameter.sortField) {
case "date":
var aDate = new Date(a.date);
var bDate = new Date(b.date);
return (asc)?(aDate - bDate):(bDate - aDate);
case "serviceno":
return (asc)?(a.serviceno-b.serviceno):(b.serviceno-a.serviceno);
default: // lexical
var aLex = String(a[sortParameter.sortField]).toLowerCase();
var bLex = String(b[sortParameter.sortField]).toLowerCase();
if (aLex < bLex) {
return (asc)?-1:1;
} else if (aLex > bLex) {
return (asc)?1:-1;
} else {
return 0;
}
}
}
The fly in the ointment with this design is Google. Once the array gets to a certain size, the sort fails with an error that the Properties service is being called too frequently. The message suggests inserting a 1s delay using Utilities.sleep()
, but the grid already takes a long time to render already - how is it going to go if the array takes 1s to decide the order of two values?
I tried reimplementing with ScriptDB, but that suffers the same problem, calls to ScriptDB service are made too frequently for Google's liking.
So how else can I implement this, without the orderFunction accessing any App Script services?