How do I restrict a trigger to a specific sheet/tab? - google-apps-script

I have written a script in google sheets that will change the time stamp in the selected cell anytime the entire spreadsheet is updated at all. However, I need the timestamp to only update when one single sheet(tab) is updated. Here is my current function:
function onEdit() {
var sheetActive = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Board")
.getRange('A28')
.setValue(new Date());
}
The name of the sheet(tab) that I want to check for an update in is "Input" and the specific cell in the tab is A1 if that matters.

The onEdit trigger triggers whenever any sheet/tab is edited. It cannot be restricted to a single sheet. However, even if it is triggered, it is possible to make it do nothing with a simple if and testing the event object that is sent on each edit, which contains the circumstances of the edit made.
Sample:
/**
* #param {GoogleAppsScript.Events.SheetsOnEdit} param1
*/
function onEdit({ range: /*edited range*/ eRange }) {
const restrictCell = 'A1',
restrictToSheet = 'Input';
if (
eRange.getA1Notation() !== restrictCell ||
eRange.getSheet().getName() !== restrictToSheet
)
return;
SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('Board')
.getRange('A28')
.setValue(new Date());
}

This is what you are looking for :
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
if ( e.source.getActiveSheet().getName() === "Input" && (row==1 && col==1) ){
e.source.getSheetByName('Board').getRange('A28').setValue(new Date())
}
}

Related

Transferring Rows between Sheets via a like Identifier

Evening everyone!
I asked this about a week back, but I think the thread got lost in the ether. We came close, but I'm trying to create a function where "Transfer a range of Rows from sheet 1 to sheet 2. Sheet 1 has order IDs in column E. G will have =unique to show me all current order IDs, with check boxes next to each unique reference. Check the box next to which ones you want to CUT over > Select a menu run add on > Run Script > all Rows from A:E that match the desired ID are moved".
[Picture Reference]
Sheet Reference
function onEdit(e) {
e.source.toast('Entry')
const sh = e.range.getSheet();
if(sh.getName() == "Reference" && e.range.columnStart == 8 && e.range.rowStart > 1 && e.value == "TRUE") {
e.source.toast('Gate1')
let rg = sh.getRange(e.range.rowStart,1,1,5)
let vs = rg.getValues();
const osh = e.source.getSheetByName("Processing");
osh.getRange(osh.getLastRow() + 1,1,1,5).setValues(vs);
rg.deleteCells(SpreadsheetApp.Dimension.ROWS);
e.range.setValue("FALSE");
}
}
Here is what we had so far. Please let me know if anyone can help, thank you!
To get all rows that match the unique ID whose checkbox was ticked, use Array.filter(), like this:
/**
* Simple trigger that runs each time the user hand edits the spreadsheet.
*
* #param {Object} e The onEdit() event object.
*/
function onEdit(e) {
if (!e) {
throw new Error(
'Please do not run the onEdit(e) function in the script editor window. '
+ 'It runs automatically when you hand edit the spreadsheet.'
+ 'See https://stackoverflow.com/a/63851123/13045193.'
);
}
moveRowsByUniqueId_(e);
}
/**
* Triggers on a checkbox click and moves rows that match a unique ID.
*
* #param {Object} e The onEdit() event object.
*/
function moveRowsByUniqueId_(e) {
let sheet;
if (e.value !== 'TRUE'
|| e.range.rowStart <= 1
|| e.range.columnStart !== 8
|| (sheet = e.range.getSheet()).getName() !== 'Reference') {
return;
}
e.source.toast('Moving rows...');
const uniqueId = e.range.offset(0, -1).getValue();
const range = sheet.getRange('A2:E');
const values = range.getValues();
const targetSheet = e.source.getSheetByName('Processing');
const _matchWithId = (row) => row[4] === uniqueId;
const valuesToAppend = values.filter(_matchWithId);
if (uniqueId && valuesToAppend.length) {
appendRows_(targetSheet, valuesToAppend);
range.clearContent();
const remainingValues = values.filter((row) => !_matchWithId(row));
range.offset(0, 0, remainingValues.length, remainingValues[0].length)
.setValues(remainingValues);
e.source.toast(`Done. Moved ${valuesToAppend.length} rows.`);
} else {
e.source.toast('Done. Found no rows to move.');
}
e.range.setValue(false);
}
For that to work, you will need to paste the appendRows_() and getLastRow_() utility functions in your script project.
It work almost like asked but :
it's using a personal lib (available below)
didn't make the part realtiv of removing range and aggregate result, I hope i can add it to the lib some day. However, empty cell are fill with -
for an obscure reason, it doesn't like the TRUE/FALSE cell, but work like a charm with 1/0 or any other texte value, regex, ...
Additional error handling are to be added if not any match or others possibilites
function onEdit(e){
console.log(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("H3").getValue())
var tableReference = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("A1").getDataRegion());
var tableReferenceId = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("G11").getDataRegion());
var tableProcessing = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Processing").getRange("A1").getDataRegion());
// get value
var id = tableReferenceId.getTableWhereColumn("Move to Sheet").matchValue(1).getWithinColumn("Unique Filter").cellAtRow(0).getValue();
var tableWithinId = tableReference.getTableWhereColumn("Shipment ID").matchValue(id)
for(var i=0 ; i < tableWithinId.length() ; i++){
var rangeRowWithinId = tableWithinId.getRow(i);
tableProcessing.addNewEntry(rangeRowWithinId);
for(var cell in rangeRowWithinId.getRange()) cell.setValue("-");
}
//reset value
tableReferenceId.getTableWhereColumn("Move to Sheet").matchValue(1).getWithinColumn("Move to Sheet").cellAtRow(0).setValue(0)
}
See below the app script file you need to create in order to use this utils function:
https://github.com/SolannP/UtilsAppSsript/blob/main/UtilsGSheetTableHelper.gs

How do i automatically set a date based on data entered from Google form?

Is there a way to automatically set a date based on data entered from form?
Tried the below, but when the data enters the sheet, the new date isnt populated except if i manually edit it.
I tried onChange, same result.
function onEdit(e){
addTimeStamp(e);
}
function addTimeStamp(e){
//Variables
var startRow = 2;
var targetColumn = 3;
var ws = "Form Responses 1";
//Get modified row and column
var row = e.range.getRow();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws){
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,7).setValue(currentDate);
if(e.source.getActiveSheet().getRange(row,6).getValue() ==""){
e.source.getActiveSheet().getRange(row,6).setValue(currentDate);
}// End IF - Check if Date created exists
}//End IF - Check column,row & worksheet
}//End Function AddTimeStamp
The onEdit() trigger does not fire when a form submits data to a linked sheet. It only fires for user edits. But you may be able to perform the same function from the onFormSubmit Trigger.
simple triggers
This is what the event object contains:
{"authMode":"","namedValues":{"Timestamp":["5/23/2021 19:53:03"],"COL1":["","2"],"Email Address":[""],"COL2":["","1"],"COL3":["","My answer"]},"range":{"columnEnd":8,"columnStart":1,"rowEnd":21,"rowStart":21},"source":{},"triggerUid":"","values":["5/23/2021 19:53:03","","","","","2","1","My answer",""]}
So you have access to range, range.columnStart, range.columnEnd and range.rowStart and range.rowEnd are always the same because it submits one row at a time. So you will have to readjustment some of the values in your current function.
So I believe this would be your new timestamp function():
function addTimeStamp(e) {
const sh = e.range.getSheet();
if(e.range.getSheet() == 'Form Responses 1') {//this will differentiate between different form submissions to the same spreadsheet
const dt = new Date();
sh.getRange(e.range.rowStart, 7).setValue(dt);
if (sh.getRange(e.range.rowStart, 6).getValue() == "") {
sh.getRange(e.range.rowStart, 6).setValue(dt);
}
}
}
This code was designed to run on the onFormSubmit trigger not the onEdit trigger. Please note as I described about that the edit performed by a form submission does not create an onedit trigger. You can only create an onedit trigger with a user edit.
Edit:
You should install this trigger for this to work (see installable triggers and form submit event object).

Count the number of changes in a column - Google Script / Sheets

First of all, I ask you to excuse me if I make some language-mistake (I'm Italian!).
I'm trying to write a script for a Google Sheet that can help me to track the number of changes of a column. I would like a counter that grows everytime a value of a cell changes. Ex:
-Column A: the cell A3 changes from "2020" to "2021"
-Column B: the cell B3 changes from 0 to 1 (o from 2 to 3, a simple +1 on the value).
I wrote this code but I cannot understand where is the error.
function onEdit(e) {
incrementCounter_(e);
}
function incrementCounter_(e) {
var stw = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Foglio2");
var c1 = stw.getRange(2, 1);
var c2 = stw.getRange(2, 2);
if (!e || !e.range) {
return;
}
var sheet = e.range.getSheet();
for (var i=2; i<=6; i++){
if (sheet.getName() === stw && e.range.getA1Notation() === stw.getrange().getvalue(i,1)) {
var cell = stw.getrange().getvalue(i,2);
cell.setValue((Number(cell.getValue()) || 0) + 1);
}
}
}
Thanks for the help!
There is no need to use two functions for this.
Code
// Copyright 2020 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function onEdit(e) {
var sheet = e.range.getSheet();
var sheetName = sheet.getName();
if (sheetName == "Foglio2" && e.range.getColumn() == 1) {
var row = e.range.getRow();
var val = sheet.getRange(row, 2).getValue();
sheet.getRange(row, 2).setValue(val + 1);
}
}
Explanation
What you want can be achieved by using the onEdit(e) trigger. The above function makes use of the e event object and checks where exactly the edit is being made. As it can be seen, the for loop is not needed in this situation as in order to get the column and row needed the getColumn() and getRow() methods have been used.
In this situation, the script checks if the sheet in which the edit has been made is Foglio2 and if an edit has been made on the A column. If the condition checks, it increments the corresponding value from the B column.
Note
Please note that getValue(value1, value2) is not a valid method and if you want to get the value for that specific range, you must pass the two parameters to the getRange() method.
Reference
Apps Script Event Objects;
Apps Script Sheet Class;
Apps Script Range Class - getValue().
Do you mean this?
function onEdit(e) {
if (e.range.getA1Notation() === 'A3') {
var range = SpreadsheetApp.getActiveSheet().getRange('B3');
var value = range.getValue();
value++;
range.setValue(value);
}
}

Column or Cell restriction if the other cell or column is blank

Hello masters/mentors/teachers
I'm having problem with my existing project. I'd like to try here if anyone can help me
Note: Timestamp is script coded
Column A
a Dropdown Cell (Not yet started, In Progress, Completed)
Column B
if Column A on the same row choose " In Progress ", the Column B will be having a timestamp (time start)
Column C
if Column A on the same row choose " Completed ", the Column C will be having a timestamp (time end)
My problem is sometimes people may forgot to put "In Progress" first and just proceed with "Completed".
Is there a way to restrict the column C Cell if the column B is blank ? therefore they can't proceed with "Completed" if the "In Progress" timestamp are not yet available and Column C will only get timestamp when Column B's no longer blank
What you can do is setup a trigger onEdit that will check when a user select 'Completed' if there is a start date. If not the script revert back the dropdown to previous value and display a warning message.
To work you must setup the onEdit() as an installable trigger.
It will looks like :
function setupTrigger() {
var sheet = SpreadsheetApp.openById(YOUR_SHEET_ID);
ScriptApp.newTrigger("control")
.forSpreadsheet(sheet)
.onEdit()
.create();
}
function control(e) {
Logger.log(e)
var range = e.range;
var rangeCol = range.getColumn();
var rangeRow = range.getRow();
// Logger.log(rangeCol)
if(rangeCol == 1){
var dateStart = range.getSheet().getRange(rangeRow,2).getValue();
var selection = e.value;
// Logger.log(dateStart);
// Logger.log(selection);
if(selection == 'Completed' && dateStart == ""){
Logger.log('User select completed and the start date is empty');
var ui = SpreadsheetApp.getUi();
ui.alert('Please select In Progress before completed, satrt date can\'t be empty.');
if(!e.oldValue){
range.getSheet().getRange(rangeRow,rangeCol).clearContent();
}else{
range.getSheet().getRange(rangeRow,rangeCol).setValue(e.oldValue)
}
}else{
Logger.log('Do nothing')
}
}else{
Logger.log('Not good column');
}
}
Install it :
Enter the sheet id on the place of YOUR_SHEET_ID.
Run the function setupTrigger()
I setup basic control on the start date but you can check if it is a date, if the value is older than now or not etc... it depends your need but this code give you the basic to implement the control you want.
Stéphane
Using the methods from the Spreadsheet Service [1] and an installable onEdit trigger [2][3], I set up the following code that will run when someone edit your Spreadsheet, if any cell in A column is change to "Not yet started" it'll protect the column C cell in that row from being edited (for anyone besides you). If the cell in A column is change to "In Progress" it'll remove the protection created before:
function onEditFunction(e) {
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
var value = e.value;
if(col == 1) {
if(value == "Not yet started") {
//Protect column C in that row
var protection = range.offset(0, 2).protect();
protection.removeEditors(protection.getEditors());
}
else if(value == "In Progress") {
var rangeC = range.offset(0, 2);
var sheet = range.getSheet();
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.getRange().getA1Notation() == rangeC.getA1Notation()) {
protection.remove();
}
}
}
}
}
function createSpreadsheetOnEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('onEditFunction')
.forSpreadsheet(ss)
.onEdit()
.create();
}
You need to run the createSpreadsheetOnEditTrigger function once to create the onEdit trigger that will run with your credentials.
[1] https://developers.google.com/apps-script/reference/spreadsheet
[2] https://developers.google.com/apps-script/guides/triggers/installable
[3] https://developers.google.com/apps-script/guides/triggers/events

Formula-based cell changes in Google Sheets is not firing onEdit script

I'm completely new to Google script writing, but I've used various posts here to piece together what I need: something that will add a time stamp to a row when a certain column changes. Here's what I'm currently using:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "test" ) { //checks that we're on the correct sheet
var r = s.getActiveCell();
if( r.getColumn() == 16 ) { //checks the column
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === '' ) //is empty?
nextCell.setValue(new Date());
}
}
}
This works perfectly when I manually change the data; however, the column that the script is monitoring pulls data from another sheet and this fails to fire the trigger/script. How can I get around this so that cells with formulas (that reference other sheets) will still fire my script?
Any help is greatly appreciated. Thanks!
The onEdit trigger works only when an actual user edits the spreadsheet. Depends of your use case, but you can be use a Time-driven trigger and set it in a recurring interval and with a function monitor the column for changes, here's an example:
function monitorColumn() {
// Add the trigger from the Resources menu in the Script Editor
// Script Editor > Resources > Current oroject's triggers > Add trigger
// [monitorColumn] - [Time-driven] - [Hour timer] - [Every hour]
var s = SpreadsheetApp.getActiveSpreadsheet();
var ss = s.getSheetByName("test"); // Get the sheet by name
// Get the values of the columns P & Q with getRange(row, column, numRows, numColumns)
var columnValues = ss.getRange(1, 16, ss.getLastRow(), 2).getValues();
// Loop through the values
for (var i = 0; i < columnValues.length; i++) {
// If cell in column P is empty and cell in column Q is not empty set todays date
if(columnValues[i][0] != "" && columnValues[i][1] == ""){
// While a range index starts at 1, 1, the JavaScript array will be indexed from [0][0].
ss.getRange(i+1,17).setValue(new Date());
}
}
}