Explain purpose of `e` event parameter in onEdit - google-apps-script

I have some question, hope guys can answer me. In this following function, I can't understand event 'e'. What is the 'e'? how we call the function or where's the function called? Give me some example, please!
function my_on_edit(e) {
var s = findSheetById_(e.gridId);
var r = e.range;
s.getRange(r.rowStart, r.columnEnd+1).setValue( s.getName() );
}
function findSheetById_(id) {
var sheets = SpreadsheetApp.getActive().getSheets();
for( var i in sheets )
if( sheets[i].getSheetId() == id )
return sheets[i];
throw 'Unable to find sheet with id: '+id;
}

Function my_on_edit is probably bound to onEdit trigger, check out Google Script triggers. List of active triggers is available in script editor in Resources menu.
On each edit action on your spreadsheet this handler is called with edit event object passed. e contain fields:
{
String user,
SpreadSheet source,
Range range,
Object value
}
You can find more detailed description at section "Spreadsheet Edit Events"

You know, I had the same question: what was that "е" in onOpen(e). I found an answer right there on Google website:
The e parameter in the function names above is an event object that is passed to the function.
https://developers.google.com/apps-script/guides/triggers.
And then they explain that the event object contains information about the context that caused the trigger to fire.

Related

Google apps script, onChange, can't pass a trigger object to a variable [duplicate]

This question already has answers here:
Google Spreadsheet Script onChange event not firing
(3 answers)
Closed 2 years ago.
This post was closed, but I have not been able to find a comparable problem elsewhere.
I'm trying to work out a script that will hide rows from an onChange() trigger. When a cell becomes "0", I want the row that cell is in to become hidden. Comments from my first post have taught me that the object passed from onChange does not contain a range. Is there a workaround that would solve this problem?
My spreadsheet has an input sheet for the backend and an output sheet for the frontend that goes to the client. I need an onChange trigger, so that as data goes to the frontend it nicely format for emailing to the client. Most importantly, I need empty ('0) rows to be hidden.
I'm new and just learning, so what I've written isn't working because onChange objects do not include a range. Thank you.
function onChange(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Client");
var cell = e.range;
var VALUE = cell.getValue();
if(VALUE == 0){
sheet.hideRow(cell);
}
}
I've also tried:
function onChange(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Client");
var cell = e.getValue();
if(cell == 0){
sheet.hideRow(cell);
}
}
There is no range value returned by an onChange trigger. Here's what the event object looks like.
{"authMode":"FULL","changeType":"INSERT_ROW","source":{},"triggerUid":"","user": {"email":","nickname":""}}
Addtionally keep in mind onChange requires an installable trigger.
onChange Event Object
It seems like you have the onChange onEdit triggers confused. The onChange trigger fires when a "larger" or structural change occurs, rather than the on changed values. From the docs:
An installable change trigger runs when a user modifies the structure of a spreadsheet itself—for example, by adding a new sheet or removing a column.
You can see from the event object specifications that there is no value passed to the programmer from the onChange trigger, and therefore the type of functionality you're looking for is not easily done.
You want to use the onEdit trigger which will function closer to what you are looking for. It will fire on any value change and give you the new and old values. onEdit is also a "simple trigger" so there is no need to install additional triggers like you do for onChange.
Using onEdit your code will look something like this:
function onEdit(e) {
val range = e.range;
val sheet = SpreadsheetApp.getActiveSheet();
if (e.value == 0) {
var rowNum = e.range.getRow();
sheet.hideRow(rowNum);
}
}

Replacement needed for eventdelete function to delete google calendar event

I want to delete selected calendar events but eventdelete function is now void in new scripts.
I have google script attached to a google spreadsheet which contains All Day event details. Collaborators use menus run the script to create or delete events in a google calendar.
The script loops through and selects the relevant events including its stored google calendar EventID and deletes or creates it in the google calendar. This all works fine.
I created a new google user account, new Google Calendar and new spreadsheet in a new Drive.
Having copied the Script to the new Sheet adjusting the CalendarID etc. it fails..
The EventDelete is now a Void function The error message is TypeError: CalendarApp.deleteEventSeries is not a function. Checking the list the function deleteEventSeries is now Void.
I have tested code and am able to edit description so the code is working as expected but I cannot find a function/method for deleting a google calendar event in the Calendar.
function DeleteCalendarEvents() {
var CalID = "myUserName#gmail.com"
var cal =CalendarApp.getCalendarById(CalID);
// I have removed code which loops each row in a google spreadsheet
// and selects events marked delete grabbing its stored eventID.
{
var EventID = ("The event ID eg scrambledLettersNumbers#google.com");
var event = cal.getEventSeriesById(EventID);
CalendarApp.deleteEventSeries();
}
}
I think you are confusing some fundamentals of OOP.
Is normal that your code is failing because the class CalendarApp has no function called deleteEventSeries(). You could try to find in the reference for the class.
What you want to delete is actually the object that you have retrieved by invoking the method getEventSeriesById in the Calendar object which type is CalendarEventSeries.
So to delete that Event series you will need to indeed call the method deleteEventSeries() but you need to use CalendarEventSeries object not CalendarApp.
function DeleteCalendarEvents() {
var CalID = "myUserName#gmail.com"
var cal =CalendarApp.getCalendarById(CalID);
var eventSeriesID = "<Id of the event Series>";
var eventSeries = cal.getEventSeriesById(eventSeriesID);
eventSeries.deleteEventSeries();
}
The functions are not deprecated as you have suggest in the comments. The returning type being void just means that the function will not return any variable which make sense because you have just deleted the event.
var a = eventSeries.deleteEventSeries();
>>> a == null // True

e.source returns Spreadsheet instead of Form object?

I create google form 'on the fly' using data in spreadsheet. Also I install trigger on submit form event.
ScriptApp.newTrigger('onSubmit')
.forForm(form)
.onFormSubmit()
.create();
onSubmit function placed in the spreadsheet script because there is no way to point the function on the form's side (I make the copy of existent form with script code but it is no use as I can't make that functions run).
Well, I process the submission event on the spreadsheet side. No problem. But when I tried to get the source of 'e' object:
function onSubmit(e) {
var response, items, i, item, hash, answer, id;
var sheet, arr, source;
sheet = SpreadsheetApp.openById(RESPONSE_SS_ID).getSheetByName(RESPONSE_SHEET);
response = e.response;
source = e.source;
Logger.log(e);
...
I get not the Form object as promissed in manual, but Spreadsheet object
Logger.log([{response=FormResponse, source=Spreadsheet, triggerUid=4071774310898422364, authMode=FULL}
Perhaps, I'm doing something wrong? How to get the Form source properly in this case?
Clearly the form is not behaving as its documentation says it does, which has been documented in Google Code Issue 4810
Luckily, there is at least one workaround, provided in the comments on that issue, which is to use the getEditResponseUrl method of the response to get to the form. Here is my implementation of the fix in the form of a function that fixes up the event object to add the missing source:
function fixBrokenEvent (event) {
if (! event.source ) {
var responseEditUrl = event.response.getEditResponseUrl(); //gets edit response url which includes the form url
var responseUrl = responseEditUrl.toString().replace(/viewform.*/,''); //returns only the form url
event.source = FormApp.openByUrl(responseUrl); //gets the submitted form id
}
return event
}
This workaround does the trick for me. Another solution would be to use the Trigger UID and search through the list of triggers from ScriptApp.getProjectTriggers() for the right trigger UID.
Something like...
function fixEventWithTriggers (event) {
ScriptApp.getProjectTriggers().forEach(function (trigger) {
if (trigger.getUniqueId()==event.triggerUid) {
event.source = FormApp.openFormById(trigger.getSourceId())
return event
}
}
}
This last workaround comes from Comment #5 on Issue 3786

Trigger an email when a cell is written into from another app (IFTTT)

So here's what I've been working on. I'm a basketball coach and have a spreadsheet that pulls in all of my players' tweets from IFTTT.com (it basically takes the RSS feed of a twitter list and when it is updated, it updates the spreadsheet).
I have been working on coding that basically says "if a player tweets an inappropriate word, email me immediately."
I've got the code figured out that if I just type in an inappropriate word, it'll turn the cell red and email me. However, I have not figured out how to get the code to email me after IFTTT automatically updates the spreadsheet with tweets.
Here is my code thus far. Right now I've just got one "trigger" word that is "players" just to try and get the spreadsheet to work. Here's the code:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();//Get the spreadsheet
var sheet = ss.getActiveSheet()//Get the active sheet
var cell = ss.getActiveCell().activate();//Get the active cell.
var badCell = cell.getA1Notation();//Get the cells A1 notation.
var badCellContent = cell.getValue();//Get the value of that cell.
if (badCellContent.match("players")){
cell.setBackgroundColor("red")
MailApp.sendEmail("antadrag#gmail.com", "Notice of possible inappropriate tweet", "This tweet says: " + badCellContent + ".");
}
}
Here is a link to the spreadsheet I'm working with right now: https://docs.google.com/spreadsheets/d/1g5XaIycy69a3T2YcWhcbBy0hYrxSfoEEz8c4-zP63O8/edit#gid=0
Any help or guidance on this is greatly appreciated! Thanks!
I originally wrote this answer for your previous question, so it includes answers to some of your comments from there, but since you're continuing to go asking the community to write this step-by-step , here's the next step.
The issue I'm running into is that if three tweets come into the spreadsheet at the same time, with my code, it's only going to update the most recent cell, not all three. Does that make sense?
Yes, it does make sense.
When an onEdit() trigger function calls Spreadsheet Service functions to get current info from the sheet, it enters a "Race condition". If any changes occur in the sheet after the change that triggered onEdit(), and the time when it gets scheduled, those changes will be visible when it runs. That's what you see when you assume that the change you're processing is in the last row - by the time you're processing it, there may be a new last row.
Good news, though - the attributes of the event object passed to onEdit contain the details of the change. (The parameter e.) See Event objects.
By using e.range and e.value you'll find you have the location and content of the edited cell that kicked the trigger. If additional tweets arrive before the trigger is serviced, your function won't be tricked into processing the last row.
In new sheets, the onEdit() can get triggered for multiple-cell changes, such as cut & paste. However unlikely that it may happen, it's worth covering.
Well, after getting the spreadsheet all setup & actually using the trigger from IFTTT, it doesn't work. :( I'm assuming it's not dubbing it as the active cell whenever it automatically pulls it into the spreadsheet. Any idea on a workaround on that?
Q: When is an edit not an edit? A: When it's made by a script. In that case, it's a change. You can add an installable on Change function to catch those events. Unfortunately, the change event is less verbose than an edit event, so you are forced to read the spreadsheet to figure out what has changed. My habit is to have the change handler simulate an edit by constructing a fake event (just as we'd do for testing), and passing it to the onEdit function.
So give this a try. This script:
handles a list of "bad words". (Could just as easily be monitoring for mentions of your product or cause.)
has an onEdit() function that uses the event object to evaluate the row(s) that triggered the function call.
colors "bad" tweets
has a function for testing the onEdit() trigger, based on How can I test a trigger function in GAS?
includes playCatchUp(e), an installable trigger function (change and/or time-based) that will evaluate any rows that have not been evaluated before. Script property "Last Processed Row" is used to track that row value. (If you plan to remove rows, you'll need to adjust the property.)
Has the sendMail function commented out.
Enjoy!
// Array of bad words. Could be replaced with values from a range in spreadsheet.
var badWords = [
"array",
"of",
"unacceptable",
"words",
"separated",
"by",
"commas"
];
function onEdit(e) {
if (!e) throw new Error( "Event object required. Test using test_onEdit()" );
Logger.log( e.range.getA1Notation() );
// e.value is only available if a single cell was edited
if (e.hasOwnProperty("value")) {
var tweets = [[e.value]];
}
else {
tweets = e.range.getValues();
}
var colors = e.range.getBackgrounds();
for (var i=0; i<tweets.length; i++) {
var tweet = tweets[i][0];
for (var j=0; j< badWords.length; j++) {
var badWord = badWords[j];
if (tweet.match(badWord)) {
Logger.log("Notice of possible inappropriate tweet: " + tweet);
colors[i][0] = "red";
//MailApp.sendEmail(myEmail, "Notice of possible inappropriate tweet", tweet);
break;
}
}
}
e.range.setBackgrounds(colors);
PropertiesService.getDocumentProperties()
.setProperty("Last Processed Row",
(e.range.getRowIndex()+tweets.length-1).toString());
}
// Test function, adapted from https://stackoverflow.com/a/16089067/1677912
function test_onEdit() {
var fakeEvent = {};
fakeEvent.authMode = ScriptApp.AuthMode.LIMITED;
fakeEvent.user = "amin#example.com";
fakeEvent.source = SpreadsheetApp.getActiveSpreadsheet();
fakeEvent.range = fakeEvent.source.getActiveSheet().getDataRange();
// e.value is only available if a single cell was edited
if (fakeEvent.range.getNumRows() === 1 && fakeEvent.range.getNumColumns() === 1) {
fakeEvent.value = fakeEvent.range.getValue();
}
onEdit(fakeEvent);
}
// Installable trigger to handle change or timed events
// Something may or may not have changed, but we won't know exactly what
function playCatchUp(e) {
// Check why we've been called
if (!e)
Logger.log("playCatchUp called without Event");
else {
// If onChange and the change is an edit - no work to do here
if (e.hasOwnProperty("changeType") && e.changeType === "EDIT") return;
// If timed trigger, nothing special to do.
if (e.hasOwnProperty("year")) {
var date = new Date(e.year, e.month, e["day-of-month"], e.hour, e.minute, e.second);
Logger.log("Timed trigger: " + date.toString() );
}
}
// Find out where to start processing tweets
// The first time this runs, the property will be null, yielding NaN
var lastProcRow = parseInt(PropertiesService.getDocumentProperties()
.getProperty("Last Processed Row"));
if (isNaN(lastProcRow)) lastProcRow = 0;
// Build a fake event to pass to onEdit()
var fakeEvent = {};
fakeEvent.source = SpreadsheetApp.getActiveSpreadsheet();
fakeEvent.range = fakeEvent.source.getActiveSheet().getDataRange();
var numRows = fakeEvent.range.getLastRow() - lastProcRow;
if (numRows > 0) {
fakeEvent.range = fakeEvent.range.offset(lastProcRow, 0, numRows);
onEdit(fakeEvent);
}
else {
Logger.log("All caught up.");
}
}

Why doesn't the trigger work for the script running a function on my google spreadsheet?

So I'm using a script on my googledoc spreadsheet that goes through a column and counts a certain number of occurrences of specified formatting. (i.e. in my spreadsheet "=myFunction()")
The function works fine, but my problem is despite setting up a trigger to run the script "onEdit", it never does it. I have to open the script up and save it each time I want it to update on my spreadsheet.
I've been looking up for hours but no one seems to have my question. There are no errors sent by the notifications. The code (though I don't think it's terribly relevant) for my function is:
function CountIfNotStrikeThrough2()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mysheet = ss.getActiveSheet();
var mydatarange = mysheet.getRange(1,1,390,1);
var numRows = mydatarange.getLastRow();
var rowindex = mydatarange.getRowIndex();
var columnindex = mydatarange.getColumnIndex();
var total =0;
for(i=rowindex;i<=numRows;i++)
{
if(mydatarange.offset(i-1, columnindex-1, 1, 1).isBlank() != true && mydatarange.offset(i-1, columnindex-1, 1, 1).getFontLine() != "line-through")
{
total++;
}
}
return total;
}
I know there's a thread here that has a comprehensive list of when the onEdit trigger doesn't activate. (here) For example, if you setup Data Validation and select an item from the list of values in the validation, it doesn't trigger the onEdit function.
Besides that, could you try a simple trigger instead of installable? This is to say, instead of explicitly associating CountIfNotStrikeThrough2() to the onEdit trigger, please try re-naming the function to "onEdit()" instead and see if that works. This creates an implicit or simple trigger. I've had trouble in the past with installable vs. simple triggers.
Also, please share the exact action you're performing to test the trigger.
Reference: https://developers.google.com/apps-script/understanding_triggers