I have been trying to get the time stamp in my google sheet every time I make an entry in the first column it should give me the date and time stamp in the fourth column. However, I don't know what I'm doing wrong all the tutorials are a year old and Google has removed the edit script feature from google Sheets' "tools" menu so I don't know how to connect the function to it properly.
Here's the code :
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColoumn();
if(col===1 && row>1 && e.source.getActiveSheet.getName()=== "Dummy1") {
e.source.getActiveSheet.getRange(row,4).setValue(new Date());
}
}
And this is the error it gives me:
TypeError: Cannot read property 'range' of undefined
onEdit # TimeStamp.gs:3
And a link of the sheet: https://docs.google.com/spreadsheets/d/1lBe7GDT7-yAS3GiBhoiVppIZe7gDkI3ihExmo5ZaEy8/edit?usp=sharing
You have a typo. It is getColumn() not getColoumn().
Also getActiveSheet is a function, so it needs parenthesis getActiveSheet().
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
if(col===1 && row>1 && e.source.getActiveSheet().getName()=== "Dummy1") {
e.source.getActiveSheet().getRange(row,4).setValue(new Date());
}
}
This solution takes advantage of the event object much better and will run much faster.
function onEdit(e) {
//e.source.toast('Entry');//these lines can be an aid to debugging
//Logger.log(JSON.stringify(e));//uncomment this to learn more about event object
const sh = e.range.getSheet();
if (e.range.columnStart == 1 && e.range.rowStart > 1 && sh.getName() == "Dummy1") {
e.range.offset(0, 3).setValue(new Date());
}
}
You cannot run functions like this from the script editor because they require the event object from the onedit trigger which is generated when you edit a cell on the spreadsheet. So to test them you simply need to create them and save them as function onEdit(e) and then edit the page to see it work or not. You can utilize e.source.toast('flags') in different locations to help you to determine where problems exist in the function.
Related
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.
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.
I have 2 Spreadsheets, namely Source and Destination. In Source, I have listed my sample products and their designated prices and are copied over to Destination using IMPORTRANGE function. Now, I'm trying to fire the OnChange trigger when there is an update on 'Destination Sheet 1'B:B only. Here's my code:
function testFunction(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Destination Sheet 1");
if (e.source.getSheetName = "Destination Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
var range = ss.getRange(e.range.getRow(), 3, ss.getLastRow());
range.setValue("There's an update");
}
}
I've also added the function testFunction on OnChange Trigger.
However, based on Event Objects Documentation there's no option for me to get the range that was updated from the events object. By "range", I mean the Destination Sheet 1'B:B. I'm getting this error:
"TypeError: Cannot read property 'getRow' of undefined at testFunction(Code:3:64)"
Desired Outcome (After an update on the pricing in 'Destination Sheet 1'!B:B):
I'm still fairly new to google scripts and I've really tried everything that I can think of but it is not getting me anywhere. Any help is greatly appreciated. Thank you in advance!
UPDATE 1:
It seems that there is no way of triggering the OnChange trigger when there is an update on a sheet that is on IMPORTRANGE function.
ALTERNATIVE:
Since it doesn't work, I was thinking of using OnEdit trigger instead on Source to handle that edit and apply my desired outcome on Destination. However, I'm having permission issues.
Exception: You do not have permission to call SpreadsheetApp.openById. Required permissions: https://www.googleapis.com/auth/spreadsheets
Here's my code:
function onEdit(e){
aSourceFunction(e);
}
function aSourceFunction(e) {
//IF stmt to make sure that OnEdit Trigger is not triggerred anywhere
if (e.source.getSheetName = "Source Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
//my attempt to call Destination Sheet 1 from Destination
var ss = SpreadsheetApp
.openByID("1QMtU6VbNQyDLXMHmaLBBDePH5l")
.setActiveSheet("Destination Sheet 1");
//desired outcome
ss.getRange(e.range.getRow(), 4, ss.getLastRow()).setValue("There's an update");
}
}
Also, I'm not sure if my concern is related but I've read somewhere that my code shouldn't have #OnlyCurrentDoc but I don't have that anywhere in my codes even in my Manifest file.
{
"timeZone": "Asia/Hong_Kong",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
Here's the scope of it:
Update 2: It worked! Initially, I have a simple OnEdit trigger. However, according to this, you need to have an installable OnEdit trigger for it to work (also fixed my code for the errors) and when I ran the code, the "permission prompt" showed up for me to access the Destination Spreadsheet. Here's my code for anyone who needs it:
function aSourceFunction(e) {
//IF stmt to make sure that OnEdit Trigger is not triggerred anywhere
if (e.source.getSheetName = "Source Sheet 1" && e.range.getRow() > 1 && e.range.getColumn() == 2){
//my attempt to call Destination Sheet 1 from Destination
var ss = SpreadsheetApp.openById("1QMtU6VbNQyDLXMHmaLBBDePH5l-4wDPUNQ827gb3jXY");
var sheet = ss.getSheetByName("Destination Sheet 1");
//desired outcome
sheet.getRange(e.range.getRow(), 4).setValue("There's an update");
}
}
Note: Go to Edit>Current project's trigger>Add Trigger using aSourceFunction as your function and OnEdit as the trigger.
From the question
However, based on Event Objects Documentation there's no option for me to get the range that was updated from the events object. By "range", I mean the Destination Sheet 1'B:B.
You are right, there is no way to get the range that was udpdated from the change event object.
But if you already know that range of interest is 'Destination Sheet 1'!B:B you can use:
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var range = spreadsheet.getRange("Destination Sheet 1!B:B");
Regarding the error message it occurs because the change event object doesn't include a range property.
Please bear in mind that (from Installable Triggers)
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.
Regarding how to make the OnChange trigger to fire on updates on Column B only, you can try to use getActiveRange() but sometimes it doesn't work. There is an open issue related to this getActiveRange() incorrect in onChange trigger for some column/row operations
Related
How to identify changed cell in onChange function
Why Class Range getValues sometimes returns [[]] when chained to Class Sheet getActiveRange?
So I have two separate sheets, one with a data list full of unique names, their location and their current status. The second would have an input to type someones name and it would pull up the row that contains their current location/status(first function), from there I could change the status and run the second function that updates the first sheet with the new status. I have been googling all day trying to cobble together something that would work and I think I am almost there on the first function but I am stuck and any help to point me in the right direction would be greatly appreciated.
Ideally the first function would trigger upon typing the name in and the second would trigger upon changing the status but I can find a manual workaround if need be.
function dataPull() {
var data = SpreadsheetApp.openById('Spreadsheet_ID'); //replace with Data spreadsheet ID
var filteredRows = data.filter(function (data) {
var employee = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('b2').getValue();
if (data[1] === employee) {
return data
}
});
SpreadsheetApp.getActiveSheet().getRange('A2:D').setValue(filter)
}
Here are some screenshots that will hopefully better explain what I am looking to accomplish.
Sample data
Type someones name in Input sheet
Pull up current status w/ function one
Update Status and run function two
Sample data updates to reflect change in status
Edit Dialog in a Sheet
There is two sections to this function. The top section triggers off of edits to the Edit sheet in column 2 and row 2 which is a dropdown that contains all of the name in the Data Sheet. An edit here cause the top section to run which reads the the data on the Data sheet and file a matching name when it finds it that line is loaded into columns 1 through 4 and row 2 of the Edit sheet. Then you can edit the status and click the checkbox to save it back to the Data sheet. The lower portion then runs which reset's the checkbox, clears the Edit range and loads the edits back into the Data Sheet.
Please take a look at the animation to see the process in action.
Just to let you know upfront, you can run this function from the script editor you must put it into you sheet and name your sheets properly. You must also set up the data validations for the two drop downs. If you try to run this function from the script editor you will get the Error: Cannot find method getsheet() of undefined
function onEdit(e) {
e.source.toast('Entry');
var sh=e.range.getSheet();
if(sh.getName()=='Edit' & e.range.columnStart==2 && e.range.rowStart==2) {
e.source.toast('flag1');
var found=false;
var dsh=e.source.getSheetByName('Data');
var vA=dsh.getRange(2,1,dsh.getLastRow()-1,dsh.getLastColumn()).getValues();
for(var i=0;i<vA.length;i++) {
if(vA[i][1]==e.range.getValue()) {
sh.getRange(2,1,1,4).setValues([vA[i]]);
found=true;
e.source.toast('Value Found at i = ' + i + 'value = ' + vA[i][1],'',-1);
break;
}
}
if(!found) {
e.source.toast('No Value found = ' + e.value);
}
Logger.log(vA);
}
if(sh.getName()=='Edit' & e.range.columnStart==5 && e.range.rowStart==2) {
e.range.setValue("FALSE");
dsh=e.source.getSheetByName('Data');
dsh.getRange(Number(e.range.offset(0,-4).getValue()+1),4).setValue(e.range.offset(0,-1).getValue());
sh.getRange(2,1,1,4).clearContent();
}
}
Data Sheet:
Edit Sheet:
Animation:
You can't use the built-in onEdit(e) trigger; as it behaves in the same manner as a custom function would eg.(Restricted to the sheet it's bound to).
You can install a trigger in your bound script, and have it execute your un-restricted function.
function importData(e){
// Get the cell where the user picked its name
var range = e.range;
// The Id of your data source sheet
var dataSheetId = 'XXXXXXXXXXXXX';
// Get all values from source sheet as an array
var data_values = SpreadsheetApp.openById(dataSheetId).getDataRange().getDisplayValues();
// Return the row with data for matching employee
var filteredRow = data_values.filter(check)[0];
// Input location data in input sheet
SpreadsheetApp.getActiveSheet().getRange(range.getRow(),range.getColumn()+1).setValue(filteredRow[2]);
}
function check(row) {
// Get the name that the user picked
var employee_name = SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
// Check if it matches and return the row
if (row[1].toLowerCase() === employee_name.toLowerCase()) {
return row
}
}
You can install the trigger from within your Apps Script by following Edit/Current project's triggers/Add trigger.
As the event source for the trigger select: "From spreadsheet"; and for function to run select "importData" (or the name of your function).
This question already has an answer here:
Trigger onEdit() with a programmatic edit
(1 answer)
Closed 3 years ago.
I'm currently working on a GAS project, and facing a strange issue.
I've set up an installable trigger that detect any change in the column A, and launch a custom function (installableOnEdit). In case the row in which the modification has been done contains datas, I call a custom function (GetProductInfo) to make an API call that will write value in different cell.
If not I just clear some other rows.
My issue is, if I manually insert datas in the A column, then it works like a charm, but in case i'm using a function to write in the A column with the .setValue (from an other custom function), the trigger does not fire.
Here is my code :
function installableOnEdit(e) {
var ss = SpreadsheetApp.getActive();
var editedRange = e.source.getActiveRange();
var editedRow = editedRange.getRow();
var editedCol = editedRange.getColumn();
var url = editedRange.getValue();
var urlColumn = 1;
if (editedCol == urlColumn){
if (url != ""){
GetProductInfo(url, "material", editedRow, editedCol);
}else {
//Reset material
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data").getRange(editedRow, editedCol+3).setValue("");
}
}
}
Any ideas ?
Thanks to tehhowch i've been able to find a solution.
That's not gonna fit for everyone, but instead to use the trigger, i simply call my custom function that retrieve the information from the API (GetProductInfo), by calling it at the end of the function that make the .setValue().