I add an onChange trigger to my Google Sheet via Tools->Script Editor -> Edit -> Current Project's trigger ->... I thought onChange is only triggered when the structure of data/sheet is changed, e.g., add or delete row and/or columns. However, the testing shows the onChange is triggered when cell value is changed as well.
So the onChange trigger behaves almost identical to the onEdit trigger. I am trying to avoid the onEdit trigger since it's triggered too often, which drags down the speed of Google Sheet.
Ideally, I'd like the onChange only being triggered when new rows and/or columns are added. Any help would be highly appreciated!
There is an event object which contains information about the context that caused a trigger when it is fired. For the case of Google Sheets, the event object has a changeType parameter which is a string describing the type of the change. As described in the Event Objects Documentation, this can take the string value of:
EDIT
INSERT_ROW
INSERT_COLUMN
REMOVE_ROW
REMOVE_COLUMN
INSERT_GRID
REMOVE_GRID
OTHER
Cooper's answer will work although as there are other change types including the catch-all OTHER, it will also make the function fire on edits such as sorting ranges or inserting images. Try something like:
function onChange(e){
if(e.changeType == 'INSERT_ROW' || e.changeType == 'INSERT_COLUMN'){
// your code here
}
else {
return;
}
}
And then set up your installable trigger as normal in Edit > Current project's triggers > + Add Trigger.
Do something like this
function myOnchangeFunction(e) {
if(e.changeType=='EDIT') {
return;
}
}
Related
I am running a python script that updates my google sheet (named 'Notifications') through the sheets api every 5 minutes. The python script first clears and resizes the sheet to only two rows and then adds the new data on top and the sheet's size gets automatically expanded. I've done that not to have empty rows if my new data is smaller in size than the old one.
So the sheet is updated every 5 minutes but if nothing new happened in my data source, the new values are going to be the same as the old. In particular, the value in 'A2' is going to be the same as before the update. If something new has happened, the value in 'A2' is going to be different. I want to track that and get a pop-up message that notifies me that something new has happened after the update.
So as far as I understand I have to use onChange because onEdit only works if the changes are made by a human and cannot track changes made by the api. I'm uncertain how to do that though.
With onEdit I've achieved it and get a pop-up if I modify the content of 'A2' manually but nothing happens when python updates my sheet.
Here's the code with onEdit:
function onEdit(e) {
if (e.range.getA1Notation() === 'A2') {
let oldValue = e.oldValue
let newValue = e.newValue
if (oldValue != newValue) {
Browser.msgBox('The value in A2 changed from ' + oldValue + ' to ' + newValue);
}
}
}
So the end goal is to get a pop-up not every time python updates my sheet, but only if there's a change in the value of 'A2' after the update.
How can I do this?
The only way to get a pop-up when a a change is made through an API is by using polling function from client-side code. One way to do this is by using a sidebar to hold the client-side code.
You should store somewhere the old values then compare with the current values.
Related
How do I make a Sidebar display values from cells?
How to get all old values for a mulitple cell range with onEdit trigger
onEdit(e) not generating trigger event when cell value changes due to inbuilt function
You can use onChange trigger with a "ValueInputOption"="USER_ENTERED" to trigger a function when a script edits a sheet. But you can't show anything in the UI from the triggered context so getting a pop up will be impossible.
I wrote script for Google Sheets which is triggered by an onEdit event.
To ilustrate my problem, let's suppose that the contents of ranges A3:A5, C3:C5 and E1:E8 were selected and then deleted at once by pressing [delete]. In this situation, the onEdit(e){...} function would be called. The problem is that object e only has information about the first edited range: e.range.getA1Notation() == 'A3:A5'.
I tried obtaining the other edited ranges by calling SpreadsheetApp.getActiveSheet().getActiveRangeList().getRanges(); but only the first range is being returned.
The way to do it would be:
function onEdit(e) {
var selection = e.source.getSelection();
var ranges=selection.getActiveRangeList().getRanges();
for (var i = 0; i < ranges.length; i++) {
Logger.log(ranges[i].getA1Notation());
}
}
However, only the first range will be returned if you run the function onEdit() instead of manually with var selection = SpreadsheetApp.getActiveSheet().getSelection();.
This seems to be a bug and has already been filed on Issuetracker:
https://issuetracker.google.com/issues/115931946
I suggest you to give it a "star" to increase visibility.
You want to retrieve all selected ranges when the OnEdit event trigger is fired.
In your case, when the cells of A3:A5, C3:C5 and E1:E8 are manually deleted, you want to retrieve the ranges of A3:A5, C3:C5 and E1:E8.
If my understanding is correct, how about this answer? Please think of this as just one of several answers.
Issue:
At first, I summarized the current situation as follows. As the sample case, it supposes the situation that the cells of A3:A5, C3:C5 and E1:E8 are deleted.
The event object of OnEdit event trigger cannot return the values of multiple ranges like A3:A5, C3:C5 and E1:E8.
When the OnEdit event trigger is fired, the script of by running the trigger returns only A3:A5 which is the first selected range.
The script is SpreadsheetApp.getActiveSpreadsheet().getSelection().getActiveRangeList().getRanges().
When the above script is run by the script editor, A3:A5, C3:C5 and E1:E8 are returned.
These were mentioned by XY6 and ziganotschka. I also think that this might be a bug.
From above situation, I guessed that when the OnEdit event trigger is fired, if the script is indirectly run by the trigger, SpreadsheetApp.getActiveSpreadsheet().getSelection().getActiveRangeList().getRanges() might return A3:A5, C3:C5 and E1:E8. By this, I thought the following workaround.
Workaround:
In this workaround, above my guess was achieved using a dialog. The flow of this workaround is as follows.
Cells of A3:A5, C3:C5 and E1:E8 on the Spreadsheet are manually deleted.
The installable OnEdit event trigger is fired and a script is run.
The script opens a dialog.
The side bar can be also used for this situation.
The script of SpreadsheetApp.getActiveSpreadsheet().getSelection().getActiveRangeList().getRanges() is run using Javascript at the dialog.
By this, running indirectly getSelection can be achieved. As the result, it was found that this workaround can retrieve all selected ranges when the OnEdit event trigger is fired.
Sample script:
The sample script for this workaround is as follows. When you use this script, please install myFunction() as the installable OnEdit event trigger.
function myFunction() {
var script = "<script>google.script.run.withSuccessHandler((e)=>{google.script.host.close()}).getSelectedRanges()</script>";
var html = HtmlService.createHtmlOutput(script).setWidth(100).setHeight(100);
SpreadsheetApp.getUi().showModalDialog(html, "sample");
}
function getSelectedRanges() {
var selection = SpreadsheetApp.getActiveSpreadsheet().getSelection().getActiveRangeList().getRanges();
var ranges = selection.map(function(e) {return e.getA1Notation()});
Logger.log(ranges) // ["A3:A5", "C3:C5", "E1:E8"]
}
When the cells of A3:A5, C3:C5 and E1:E8 on the active Spreadsheet are manually deleted, a dialog is opened by myFunction(), and Javascript of the dialog runs getSelectedRanges(). Then, the dialog is automatically closed.
References:
Installable Triggers
Class Ui
Class HtmlService
Class Selection
If I misunderstood your question and this was not the direction you want, I apologize.
Have successfully implemented OnEdit/OnChange triggers. However, these require an actual change to a cell.
It does not look like there is an analogous OnSelectionChanged trigger that fires when a new cell selection is made.
Any thoughts on how one might implement?
Good news. There is a new Simple Trigger, check this release note:
A new simple trigger, onSelectionChange(e), has been added for
Google Sheets. The onSelectionChange(e) trigger runs automatically
when a user changes the selection in a spreadsheet.
The following code illustrates how to use this trigger:
function onSelectionChange(e) {
var range = e.range;
if(range.getNumRows() === 1 && range.getNumColumns() === 1) {
SpreadsheetApp.getActiveSheet().clearFormats();
range.setFontColor("red").setFontWeight("bold")
}
}
If you add this function in your sheet bounded script the selected cell will always be highlighted as bold red. Please, note that this is just an example. The clearFormats() call will clear the format of all cells in the active sheet, so take care with it.
Is it possible to listen to "worksheet tab renamed" events? It will be relevant for us to know when this event occurs as we display this information in our own UI addon.
The onEdit() function is not triggering on sheet renaming.
The onEdit trigger fires when the content of some cell is changed. All other changes must be detected by onChange trigger. The corresponding event object has changeType equal to 'OTHER' when the change is renaming a sheet. As an example, set onChange trigger to this function (can be done programmatically)
function change(e) {
if (e.changeType == 'OTHER') {
// do something
}
}
The event object does not say what was renamed and to what (and OTHER may be some other type of change, too). So, you will need to have the list of sheet names stored in script properties, and when the event fires, compare it to the new one.
I am writing Google app script of Google spreadsheet, but I encounter some problem. I try to trigger the "onEdit" function by a cell whose value is referenced by other cell. However, it doesn't seem to work well. For example, the value of A cell is referenced by B cell(the value of A is "='B'"), and if I changed the value of B cell, A cell will change accordingly, but fail to trigger the onEdit function of A cell. I am thinking if it is because the change made automatically by spreadsheet is not an event that will trigger the onEdit function.
Does anybody know other solutions to solve this problem?
Thanks in advance!
onEdit will run any time a cell is updated (as you assumed). Using your logic above, would it work to set a check in the onEdit event to see what cell was changed, and if it was B, could you make the logical assumption that A changed as well?
function onEdit(event)
{
// 'event' contains information about the edit that took place
// Here, we read the 'source' and get the active range (our changed cell)
// We then pull out the string form of the notation to get the cell that changed
var changedCell= event.source.getActiveRange().getA1Notation();
Browser.msgBox(changedCell)
if (changedCell == 'B1') {
// If the changed cell is the one that affects A, do something
Browser.msgBox('You changed B1, and this will affect A');
}
}
As you've mentioned, the onEdit would be triggered for cell B not cell A. You can use this to read off cell A or you can have a function triggered every minute to read cell A.