i have 3 sheets my spreadsheet. Names is sheet1, sheet2, sheet3. And i have a function "onChange()". i install onChange function at triggers. i want run onChange function if only when add new row at sheet2 . But not working properly. if i write sheet1 or sheet2 or sheet3 anything always run my onChange function.
function onChange(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(),1,1,2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(),1,1,4).setValues(sheet2lr)}
I believe your goal as follows.
You want to run the script of onChange() when new row is inserted to "sheet2".
Your function of onChange() has already been installed as the OnChange installable trigger.
In this case, I thought that changeType and source of the event object can be used for achieving your goal. When this is reflected to your script, it becomes as follows.
Modified script:
function onChange(e) {
if (e.changeType != "INSERT_ROW" || e.source.getActiveSheet().getSheetName() != "sheet2") return; // Added
// do something
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2"); // Or e.source.getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(), 1, 1, 2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1"); // Or e.source.getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(), 1, 1, 4).setValues(sheet2lr)
}
In this modified script, when new row is inserted to "sheet2", the script below the if statement is run.
When you want to run this script, please insert new row to the sheet of "Sheet2".
Note:
If above modified didn't work, please reinstall the OnChange trigger to the function onChange.
For your script, I think that you can also the event object of e.source instead of SpreadsheetApp.getActiveSpreadsheet().
Reference:
Event Objects
Added:
About your following comment,
thanks Tanaike for help me. if the user adds a row to sheet2, the onchange function works(only change equlas oparator != change ==).actually another application is adding a row to sheet2. when if i use 1 control e.source.getActiveSheet().getSheetName() == "sheet2") working but if i use second control not fire onChange e.changeType != "INSERT_ROW".
In the current stage, in your situation, OnChange trigger can be used when the row is manually inserted and the row is inserted with Sheets API which is not Google Spreadsheet service (SpreadsheetApp). So from your comment, in your situation, I thought that the row is inserted with SpreadsheetApp. If my understanding is correct, I would like to propose the following 2 patterns.
Pattern 1:
In this pattern, your current script of onChange() is called from the function of actually another application is adding a row to sheet2. When your script of actually another application is adding a row to sheet2 is including in the same Google Apps Script project of onChange() and it supposes that the function name is sample(), you can modify as follows.
function sample() {
// do something of `actually another application is adding a row to sheet2`
onChange();
}
function onChange(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(),1,1,2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(),1,1,4).setValues(sheet2lr)
}
Pattern 2:
In this pattern, the row is inserted with Sheets API in your script of actually another application is adding a row to sheet2. In this case, you can convert the script for inserting row using InsertDimensionRequest of the batchUpdate method. Ref When this script is run, my proposed script can be used. Because when new row is inserted with Sheets API, the OnChange trigger is fired. In this case, I cannot understand about your script of actually another application is adding a row to sheet2. By this, I just propose the method for achieving your goal using Sheets API.
Related
Looking to get the name of the newly added sheet (manually added) using an installable trigger.
ChangeType with INSERT_GRID worked fine for me (tested by outputting some random value when sheet gets added) but when I try getting the name of the newly added sheet, it instead gives me the value of the FIRST sheet's name in the document.
Any alternatives? Have heard that this might be a previous bug in App Script.
function log(e)
{
if (e.changeType === "INSERT_GRID") {
var news = e.source.getActiveSheet().getName(); //fails to get correct value
e.source.getSheetByName("Client").getRange(1, 1).setValue(news);
}}
I believe your goal as follows.
You want to detect the sheet name of added sheet using the OnChange event trigger.
In your current issue, you cannot retrieve the sheet name of added sheet. When this is the bug or specification, you want to know about the alternatives for achieving your goal.
For this, how about this answer?
Issue and workaround:
Unfortunately, in the current stage, it seems that the event object of OnChange event trigger has not information about the added sheet. By this, when your script is used, the sheet name of the 1st tab is retrieved. I'm not sure whether this is the bug or the current specification.
So in order to achieve your goal, I would like to propose the following workaround. The flow of this workaround is as follows.
At first, the initial sheet names are saved to PropertiesService.
When the sheet is added, the aded sheet is retrieved by comparing the current sheets and initial sheets retrieved from the PropertiesService.
Sample script:
In order to use this script, please close the Google Spreadsheet and open it again. By this, onOpen is run and the initial sheet names are saved to the PropertiesService. Then, when the sheet is added, the added sheet is retrieved and the sheet name of the added sheet is put to the cell.
function saveCurrentSheets(prop, spreadsheet) {
const sheets = spreadsheet.getSheets().reduce((o, s) => Object.assign(o, {[s.getSheetName()]: true}), {});
prop.setProperty("sheets", JSON.stringify(sheets));
}
function onOpen(e) {
saveCurrentSheets(PropertiesService.getScriptProperties(), e.source);
}
function log(e) {
if (e.changeType === "INSERT_GRID") {
const prop = PropertiesService.getScriptProperties();
const oldSheets = prop.getProperty("sheets");
if (!oldSheets) {
saveCurrentSheets(prop, e.source);
return;
}
const oldSheetsObj = JSON.parse(prop.getProperty("sheets"));
const addedSheet = e.source.getSheets().filter(s => !oldSheetsObj[s.getSheetName()])[0];
const news = addedSheet.getSheetName();
e.source.getSheetByName("Client").getRange(1, 1).setValue(news);
saveCurrentSheets(prop, e.source);
} else if (e.changeType === "REMOVE_GRID") {
saveCurrentSheets(PropertiesService.getScriptProperties(), e.source);
}
}
In this script, please install the OnChange event trigger to the function of log.
In this script, when the existing sheet is deleted, the values of PropertiesService are updated by the current sheet names.
Note:
When there are a lot of sheets in the Google Spreadsheet, I think that the initial sheet names are required to be a file instead of the PropertiesService, because of "Properties value size is 9kB / val." and "Properties total storage is 500kB / property store.". Please be careful this.
References:
Event object of OnChange
Class Properties
Quotas for Google Services
That's the normal behavior. It always returns e.source.getSheets()[0]; Which is the same thing if you get the active sheet when you openById();
i have 3 sheets my spreadsheet. Names is sheet1, sheet2, sheet3. And i have a function "onChange()". i install onChange function at triggers. i want run onChange function if only when add new row at sheet2 . But not working properly. if i write sheet1 or sheet2 or sheet3 anything always run my onChange function.
function onChange(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(),1,1,2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(),1,1,4).setValues(sheet2lr)}
I believe your goal as follows.
You want to run the script of onChange() when new row is inserted to "sheet2".
Your function of onChange() has already been installed as the OnChange installable trigger.
In this case, I thought that changeType and source of the event object can be used for achieving your goal. When this is reflected to your script, it becomes as follows.
Modified script:
function onChange(e) {
if (e.changeType != "INSERT_ROW" || e.source.getActiveSheet().getSheetName() != "sheet2") return; // Added
// do something
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2"); // Or e.source.getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(), 1, 1, 2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1"); // Or e.source.getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(), 1, 1, 4).setValues(sheet2lr)
}
In this modified script, when new row is inserted to "sheet2", the script below the if statement is run.
When you want to run this script, please insert new row to the sheet of "Sheet2".
Note:
If above modified didn't work, please reinstall the OnChange trigger to the function onChange.
For your script, I think that you can also the event object of e.source instead of SpreadsheetApp.getActiveSpreadsheet().
Reference:
Event Objects
Added:
About your following comment,
thanks Tanaike for help me. if the user adds a row to sheet2, the onchange function works(only change equlas oparator != change ==).actually another application is adding a row to sheet2. when if i use 1 control e.source.getActiveSheet().getSheetName() == "sheet2") working but if i use second control not fire onChange e.changeType != "INSERT_ROW".
In the current stage, in your situation, OnChange trigger can be used when the row is manually inserted and the row is inserted with Sheets API which is not Google Spreadsheet service (SpreadsheetApp). So from your comment, in your situation, I thought that the row is inserted with SpreadsheetApp. If my understanding is correct, I would like to propose the following 2 patterns.
Pattern 1:
In this pattern, your current script of onChange() is called from the function of actually another application is adding a row to sheet2. When your script of actually another application is adding a row to sheet2 is including in the same Google Apps Script project of onChange() and it supposes that the function name is sample(), you can modify as follows.
function sample() {
// do something of `actually another application is adding a row to sheet2`
onChange();
}
function onChange(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet2");
var sheet2lr = ss.getRange(ss.getLastRow(),1,1,2).getValues();
var sheet1lr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1");
sheet1lr.getRange(getLastRow(),1,1,4).setValues(sheet2lr)
}
Pattern 2:
In this pattern, the row is inserted with Sheets API in your script of actually another application is adding a row to sheet2. In this case, you can convert the script for inserting row using InsertDimensionRequest of the batchUpdate method. Ref When this script is run, my proposed script can be used. Because when new row is inserted with Sheets API, the OnChange trigger is fired. In this case, I cannot understand about your script of actually another application is adding a row to sheet2. By this, I just propose the method for achieving your goal using Sheets API.
Looking to get the name of the newly added sheet (manually added) using an installable trigger.
ChangeType with INSERT_GRID worked fine for me (tested by outputting some random value when sheet gets added) but when I try getting the name of the newly added sheet, it instead gives me the value of the FIRST sheet's name in the document.
Any alternatives? Have heard that this might be a previous bug in App Script.
function log(e)
{
if (e.changeType === "INSERT_GRID") {
var news = e.source.getActiveSheet().getName(); //fails to get correct value
e.source.getSheetByName("Client").getRange(1, 1).setValue(news);
}}
I believe your goal as follows.
You want to detect the sheet name of added sheet using the OnChange event trigger.
In your current issue, you cannot retrieve the sheet name of added sheet. When this is the bug or specification, you want to know about the alternatives for achieving your goal.
For this, how about this answer?
Issue and workaround:
Unfortunately, in the current stage, it seems that the event object of OnChange event trigger has not information about the added sheet. By this, when your script is used, the sheet name of the 1st tab is retrieved. I'm not sure whether this is the bug or the current specification.
So in order to achieve your goal, I would like to propose the following workaround. The flow of this workaround is as follows.
At first, the initial sheet names are saved to PropertiesService.
When the sheet is added, the aded sheet is retrieved by comparing the current sheets and initial sheets retrieved from the PropertiesService.
Sample script:
In order to use this script, please close the Google Spreadsheet and open it again. By this, onOpen is run and the initial sheet names are saved to the PropertiesService. Then, when the sheet is added, the added sheet is retrieved and the sheet name of the added sheet is put to the cell.
function saveCurrentSheets(prop, spreadsheet) {
const sheets = spreadsheet.getSheets().reduce((o, s) => Object.assign(o, {[s.getSheetName()]: true}), {});
prop.setProperty("sheets", JSON.stringify(sheets));
}
function onOpen(e) {
saveCurrentSheets(PropertiesService.getScriptProperties(), e.source);
}
function log(e) {
if (e.changeType === "INSERT_GRID") {
const prop = PropertiesService.getScriptProperties();
const oldSheets = prop.getProperty("sheets");
if (!oldSheets) {
saveCurrentSheets(prop, e.source);
return;
}
const oldSheetsObj = JSON.parse(prop.getProperty("sheets"));
const addedSheet = e.source.getSheets().filter(s => !oldSheetsObj[s.getSheetName()])[0];
const news = addedSheet.getSheetName();
e.source.getSheetByName("Client").getRange(1, 1).setValue(news);
saveCurrentSheets(prop, e.source);
} else if (e.changeType === "REMOVE_GRID") {
saveCurrentSheets(PropertiesService.getScriptProperties(), e.source);
}
}
In this script, please install the OnChange event trigger to the function of log.
In this script, when the existing sheet is deleted, the values of PropertiesService are updated by the current sheet names.
Note:
When there are a lot of sheets in the Google Spreadsheet, I think that the initial sheet names are required to be a file instead of the PropertiesService, because of "Properties value size is 9kB / val." and "Properties total storage is 500kB / property store.". Please be careful this.
References:
Event object of OnChange
Class Properties
Quotas for Google Services
That's the normal behavior. It always returns e.source.getSheets()[0]; Which is the same thing if you get the active sheet when you openById();
On the advice of someone below, I am editing this post:
My initial goal is that when my Google sheet is opened, and every time it is opened, I would like the values of several Data Validation dropdown menus, currently located in Cells A10, A15, and A20, to be set to the option of "Select" -- which is a word in the validation range, along with 2 other text values.
I have been informed that this needs to be declared at a global scope -- but I am a complete script novice and, frankly, have no idea as to how to make this work.
Any advice will be greatly appreciated.
function onOpen() {
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
Note for new readers:
The original code on the question was this
//When the sheet is opened, the contents of Cell A2 are cleared and the values in the Data Validation dropdown menus in Cells A10, A15, and A20 are set to the default "Select"
function myFunction() {
function onOpen() {
SpreadsheetApp.getActiveSheet().getRange('A2').clearContent();
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
}
//When the contents of Cell A2 are edited (changed), the values in the Data Validation dropdown menus in Cells A10, A15, and A20 are set to the default "Select"
function onEdit(e) {
var ss = SpreadsheetApp.getActive()
var sheet = SpreadsheetApp.getActiveSheet()
var cell = sheet.getRange('A2')
var cellContent = cell.getValue()
if(cellContent === (edit) {
SpreadsheetApp.getActiveSheet().getRange('A10').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A15').setValue('Select');
SpreadsheetApp.getActiveSheet().getRange('A20').setValue('Select');
}
}
Simple triggers should not be declared as local functions of another function, they should be declared at the global scope.
In other words, don't put onOpen and onEdit inside of myFunction.
A function on the global scope in a Google Apps Script script looks like this:
NOTE: Only one code line is included inside onOpen code block {} for simplicity. It could have any number of code lines that takes no more than 30 seconds to execute.
By the other hand simple triggers has several limitations so maybe instead of simple triggers you should consider to use installable triggers. To learn about Google Apps Script triggers please read https://developers.google.com/apps-script/guides/triggers
Also, you should bear in mind the real-time collaboration features of Google Sheets. If one user has opened the spreadsheet and another user open the same spreadsheet, the onOpen, simple and installable triggers, will be triggered and could change what the first user already edited.
After some testing I was able to make it work. As Ruben said, onOpen, onEdit cannot be inside any other function. These specify already an action so when an action of onOpen or onEdit the script will be running automatically when the spreadsheet is opened or edited.
You can check more information about Apps Script Triggers https://developers.google.com/apps-script/guides/triggers but in this specific case onEdit will run every time any cell is updated unless you specify the cell you want to run the script by using Event Objects for more information check https://developers.google.com/apps-script/guides/triggers/events.
For this specific scenario since you want the script to run when the cell is updated you have to check if that cell is being updated by using e.range.getA1Notation() the getA1Notation() returns the range in A1 notation for more information check https://developers.google.com/apps-script/reference/spreadsheet/range#getA1Notation() there is an example that will make you understand the following script.
function onOpen(e){
var app = SpreadsheetApp.getActiveSpreadsheet();
var sheet = app.getActiveSheet();
sheet.getRange("A2:A2").clearContent();
sheet.getRange("A10:A10").setValue("Select");
sheet.getRange("A15:A15").setValue("Select");
sheet.getRange("A20:A20").setValue("Select");
}
function onEdit(e){
var app = SpreadsheetApp.getActiveSpreadsheet();
var sheet = app.getActiveSheet();
var A2 = sheet.getRange("A2:A2");
console.log(e.range.getA1Notation());
if (e.range.getA1Notation() === "A2"){
console.log("A2 Updated");
sheet.getRange("A10:A10").setValue('Select');
sheet.getRange("A15:A15").setValue('Select');
sheet.getRange("A20:A20").setValue('Select');
}
}
the onOpen function will be running every time the spreadsheet is opened, and the onEdit function will be executed every time the cell "A2" changes its value. Basically you are checking if the range with A1 Notation is equals to "A2" then it will run the code within the if statement, otherwise the function will be executed but will take no action since the cell A2 is still without any change. By doing this I am not sure if there is a limit of this kind of executions but that is something you can investigate further, I don't think it will affect since this should be running on the client side not on a server side.
I hope that helps, greetings.
I'm trying to program a spreadsheet so that whenever I add a new entry to it, it copies the data from that cell into another spreadsheet. Here's my code (that doesn't seem to work):
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var target = SpreadsheetApp.openById("*************");
var target_sheet = target.getSheetByName("Dashboard");
var last_row = target_sheet.getLastRow();
target_sheet.insertRowAfter(last_row);
var target_range = target_sheet.getRange("A"+(last_row+1)+":G"+(last_row+1));
e.range.copyTo(target_range);
}
Any ideas what I'm doing wrong?
There are two types of triggers in Apps Scripts - simple triggers and installable triggers.
onEdit() is an example of a simple trigger (the name is implicit). And there are limits to what you can do inside a simple trigger. And that includes opening another spreadsheet.
So, coming to the solution.
Rename your function to something else, say onEdit1() . And set up an installable trigger manually and set it to run each time the spreadsheet is edited.
Hint: Use Resources --> Current project's triggers from the script editor