Event on rename worksheet tab not triggered - google-apps-script

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.

Related

Google Sheet Script onChange is triggered when cell value is changed

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;
}
}

In AppScript, when multiple ranges are edited at once, How is rangeList obtained?

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.

Trigger that fires when a new cell selection is made

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.

Google Sheets: noticing and acting upon new table added and table renamed

The title is the question.
I'm working on a spreadsheet in Google Drive where we got a central sheet named Name registry with names of the people who ever visited some place where notebooks are left at for them to write their name down on. The sheet calculates various stats, too.
The sheets for different places will be named as whatever the specific notebooks were named. The name registry saves us writing and fixing by referencing the cell of a particular name.
I'm sure there won't be anywhere like 255 sheets (which should be a lifted limit in the new Sheets), but I came across an idea how to have it automated:
1) Optionally a new sheet is added and the spreadsheet notices it.
2) Once either
2.1) the new sheet is renamed, the `Name registry` will auto-name one of the free columns as the renamed sheet; or
2.2) an old sheet is renamed, the sheet's old column is renamed in the `Name registry`
I did check on things, but the documentation is, frankly, brainlessly organized, so the only option is to use Google on its own resources.
For example, https://developers.google.com/apps-script/understanding_events implies it can't be done anyhow.
You should know that there is, indeed, a trigger that fires when inserting one sheet (I'm guessing that's what you call "table" in your question).
Re-check the docs for the spread sheet Change event, this event only fires if you use an installed trigger. To use it (copied from the docs)
Open or a create a new spreadsheet.
Click the Unsaved spreadsheet dialog box and change the name.
Choose Tools > Script Editor and write the function you want to run.
Choose Resources > Current project's triggers. You see a panel with the message No triggers set up. Click here to add one now.
Click the link.
Under Run, select the function you want executed by the trigger (for example myFunction).
Under Events, select From Spreadsheet
From the next drop-down list, select On Change.
Click Save.
Please notice that this trigger is not the same as the simple onEdit one.
Also, bear in mind that your callback function must receive the event parameter:
function myFunction(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheetByName("Sheet1").getRange("A1").setValue(e.changeType);
}
You'll see that there are different types of changes: EDIT, INSERT_ROW, INSERT_COLUMN, REMOVE_ROW, REMOVE_COLUMN, INSERT_GRID, REMOVE_GRID, OTHER. You're looking for INSERT_GRID for inserting new sheet and OTHER for renaming it.
Having said that, the problem here will be detecting the rename of one sheet as its an OTHER type, so you may easily trigger the function when you don't really want to do it (for example changing background color, also fires the onChange event with OTHER edit type). My advice is that you don't try to detect the createion/change but create a custom menu entries for handling the creation and renaming of sheets:
function onOpen()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "Create sheet", functionName: "createNewSheet"},
{name: "Rename current Sheet", functionName: "renameCurrentSheet"}
];
ss.addMenu("Custom operations", menuEntries);
}
function createNewSheet()
{
// create the new sheet
// also execute the logic you want when new sheet is created
}
function renameCurrentSheet()
{
// rename current sheet
// also execute the logic you want when sheet is renamed
}
Hope I put some light to your problem.

How to trigger the onEdit event of a referenced cell?

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.