First server provoke
When you’re ready to start watching for changes, the server will emulate a change to get the whole ball rolling. Here’s the Server.init code, which will provoke a push back to the client with the kind of data described in Pushing changes from Google Sheets server to client
// initialize ns.init = function () { const sf = ns.sfInit(); // create an empty item by simulating an event const pack = sf.create (sf.ssChangePack ({ source: SpreadsheetApp.getActiveSpreadsheet(), changeType:"INITIALISE" })); if (!pack.ok) throw JSON.stringify (pack); // add the trigger Triggers.installChangeTrigger ("efxChanger"); // just return the alias and reader keys since we wont be allowing writing return { reader:pack.readers[0], alias:pack.alias }; };
This is provoked from initializing the client
ns.init = function() { // handle for efx ns.state.efx = EffexApiClient; spinCursor(); //first init the server return Provoke.run("Server", "init") .then(function(keys) { resetCursor(); // now we can save those keys for later ns.state.efx.setKeys(keys); // and start watching for changes return ns.state.efx.on("update", keys.alias, keys.reader, Client.poked, { type: "push" }); }) ["catch"](function(err) { resetCursor(); App.showNotification("failed to init server", err); }); };
Notice that initializing the server returns some keys so the client knows which item to watch for changes on, and also an access key to authorize it.
What happens on change detection
The client watches for changes using efx.on
// and start watching for changes return ns.state.efx.on("update", keys.alias, keys.reader, Client.poked, { type: "push" });
which fires Client.poked when anything changes. It’s in that function that you decide whether to go back to the server for a data update and re-render the add-on.
The Server code
The entire server namespace is here, and is specific to my demo example, but the principle will be the similar for most use cases
/** * installs the changer * public tag because we cant address namespaces here */ function efxChanger(event) { return Triggers.efxChanger (event); } /** * * all server side from this namespace */ var Server = (function(ns) { ns.fetchKeys = { // where to get keys getAccessKeys: function () { return PropertiesService.getScriptProperties().getProperty('sf_efx_keys'); }, // how to get the spreadsheet ID getId: function () { return SpreadsheetApp.getActiveSpreadsheet().getId() } }; ns.sfInit = function () { // get a shortcut const sf = cSheetEfx.SheetEfx; // start off an item that'll be used for tracking return sf.init({ // this should be how to get the writerkeys fetchKeys:ns.fetchKeys, // how long new created data items shuld live for itemSeconds:30 * 60, }); }; // initialize ns.init = function () { const sf = ns.sfInit(); // create an empty item by simulating an event const pack = sf.create (sf.ssChangePack ({ source: SpreadsheetApp.getActiveSpreadsheet(), changeType:"INITIALISE" })); if (!pack.ok) throw JSON.stringify (pack); // add the trigger Triggers.installChangeTrigger ("efxChanger"); // just return the alias and reader keys since we wont be allowing writing return { reader:pack.readers[0], alias:pack.alias }; }; /** * set the active sheet and cell * @param {string} ssid the ss * @param {string} sheetId the sheet id * @param {string} the a1range to set * @param {string} method the method to use * @param {boolean} goThere whether to go there */ ns.makeRangeFocus = function (ssId , sheetId , a1Range ,method, goThere) { // get a shortcut const sf = cSheetEfx.SheetEfx; const s = sf.findSheet (ssId , sheetId, SpreadsheetApp); // if there's a range given if (a1Range && s.sheet) { s.range = s.sheet.getRange (a1Range); } // use the active range, but we'll need to set the sheet first to find it else if (s.sheet) { const currentSheet = s.ss.getActiveSheet(); const currentRange = currentSheet.getActiveRange(); s.ss.setActiveSheet(s.sheet); s.range = s.sheet.getActiveRange(); if (!goThere) { currentSheet.setActiveRange(currentRange); } } //set it var pack; if (goThere){ s.sheet.setActiveRange(s.range); pack = Triggers.efxChanger({ source:s.ss }, method); } else { // don't actually go there but make it look like a change // for the client // this allows it to show a different sheet // than the one on screen pack = Triggers.efxChanger({ source:s.ss, sheetId:s.sheet.getSheetId(), a1Range:s.range.getA1Notation() },method); } return pack; }; /** * will be called from the client to get the sheet values of a given sheet */ ns.getSheetValues = function (ssId , sheetId , a1Range, method) { // get a shortcut const sf = cSheetEfx.SheetEfx; var range = sf.findRange (ssId , sheetId , a1Range, SpreadsheetApp).range; method = method || "getValues"; if (typeof range[method] !== "function") throw 'method ' + method + ' is invalid'; return range[method](); }; return ns; })(Server || {});
For more like this, see Google Apps Scripts snippets.
Continue reading about Pushing changes from Google Sheet server to client here