I have one workbook with multiple sheets and I've been running some simple scripts on two of those sheets (those sheets are basically copies of one another).
Once script creates a dropdown based on the value entered in one of the cells, while the second script adds a timestamp based on when the new dropdown was edited.
Initially I had two onEdit functions running on the first sheet.
Then I created a copy of these functions, with small amendments, to run on another sheet(same workbook)
Given that I don't really know how to have a separate script per worksheet I now have a one script with 4 similar onEdit functions.
Since I'm a total newbie I'm sure there's a better, more efficient way to write this code.
I'd appreciate your help in optimizing this.
function onEdit(e) {
addTimestamp(e);
addTimestamp2(e);
Dropdown(e);
Dropdown2(e);
}
function addTimestamp(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker1";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function addTimestamp2(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker2";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function Dropdown(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker1"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
function Dropdown2(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker2"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
here is a more efficient way to replace the two addTimestamp functions:
function onEdit(e) {
var activeSheet = e.source.getActiveSheet();
var activeSName = activeSheet.getName();
var row = e.range.get.Row();
var col = e.range.getColumn();
if ((activeSName == 'Tracker1' || activeSName == 'Tracker2') && col === 10 && row >= 2) {
activeSheet.getRange(row, 11).setValue(new Date());
}
}
You could give the two Dropdown functions a shot and see if you can do something similar. Nothing like learning-by-doing. :-D
Just be careful that in onEdit you are passing e to these Dropdown functions. But not using them in these functions.
Related
I've been trying to make dependent drop down lists using App script. When I select my independent drop down option, executions shows "Completed", yet it won't add in the dependent drop down in the column beside it.
I can't seem to figure out what's going wrong here:
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Exercise Index");
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 1 && activeCell.getRow() > 3 && ss.getSheetName()=="Weekly Template"){
activeCell.offset(0,1).clearContent().clearDataValidations();
var categories = datass.getRange(1,1,1,datass.getLastColumn()).getValues();
var catIndex = categories[0].indexOf(activeCell.getValue())+1;
if(catIndex != 0){
var validationRange = datass.getRange(2,catIndex,datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0,1).setDataValidation(validationRule);
}
}
}
Any help would be greatly appreciated as this is my first go at App Script.
I did manage to get it functioning as intended. Here's the script in case anyone comes across this in the future:
function onEdit(){
var tabLists = "Exercise Index";
var spreadsheet = SpreadsheetApp;
var activeSheet = spreadsheet.getActiveSpreadsheet().getActiveSheet();
var data = spreadsheet.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = activeSheet.getActiveCell();
if(activeCell.getColumn() == 1 && activeCell.getRow() > 3 && activeSheet.getSheetName().includes("Week")){
activeCell.offset(0, 1).clearDataValidations();
var makes = data.getRange(1, 1, 1, data.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) + 1;
if(makeIndex != 0){
var validationRange = data.getRange(2, makeIndex, data.getLastRow());
var validationRule = spreadsheet.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
Am havong some trouble creating a script to automate timestamp on GSheet. I have 2 columns - AD & AE, that I want it to have the date timestamp for when column L and AA are updated. Have tried the following script but doesn't seem to work.
function onEdit(e) {
addTimestamp(e);
}
function addTimestamp(e){
var startRow = 3;
var targetColumn = 12;
var targetColumn2 = 27;
var ws = "Brand List";
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,30).setValue(currentDate)
}
if (col === targetColumn2 && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,31).setValue(currentDate)
}
}
Your script is working fine, but it seems that you're getting an error saying TypeError: Cannot read property 'range' of undefined.
This happens because the script is using a trigger to execute the function onEdit() whenever the spreadsheet is edited.
When executed, this trigger will pass an event object (e) as an argument to the function, which includes data such as the cell that was edited.
However, this event object is only passed if the sheet is edited, which doesn't happen if you run the function from the script editor.
So, to make it work, you need to edit the sheet as you'd do when using the script.
If you actually want to test it separately, you'd need to explicitly create this event object and pass it to the onEdit() function: How can I test a trigger function in GAS?
For those interested in the script used.
function onEdit(e) {
addTimestamp(e);
addTimestamp2(e);
}
function addTimestamp(e){
var startRow = 3;
var targetColumn = 12;
var targetColumn2 = 27;
var ws = "Brand List";
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,30).setValue(currentDate)
}
}
function addTimestamp2(e){
var startRow = 3;
var targetColumn = 12;
var targetColumn2 = 27;
var ws = "Brand List";
var row = e.range.getRow();
var col = e.range.getColumn();
if (col === targetColumn2 && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,31).setValue(currentDate)
}
}
I'm trying to connect 2 spreadsheets, such that, I can perform a sumif function on one spreadsheet and get the result on the current one. I'm using onEdit installable trigger on function when since the simple trigger doesn't work. The dependant dropdown and also the sumif through a for loop works, but I can't seem to get the function when to call the function sum in order to set value on a cell. The sum function works separately and gives the result, but when executing onEdit it doesn't.
Also, is this the correct way to execute sumif? Is there any faster way(I require to execute it around 600-1000 times, here I kept it just 10 to test it out, which again is slow)?
My code follows:
'''
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var stck = SpreadsheetApp.openById("1WGbEo1Xr99HwHY_4ZaeTRIgCOuNcjVfqCzZx4dcQX4I");
var unisensor = stck.getSheetByName("Unisensor");
var ssOptions = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Product List");
var options1 = ssOptions.getRange(2,1,ssOptions.getLastRow() - 1,2).getValues();
function When(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var ssName = activeCell.getSheet().getName();
if (ssName=="Stock" && r==2 && c==1){
ss.getRange(r,2).clearContent();
ss.getRange(r,2).clearDataValidations();
var filteredOptions = options1.filter(function(o){return o[0] === val});
var listToApply = filteredOptions.map(function(o){return o[1]});
console.log(listToApply);
var cell = ss.getRange(r,2);
applyValidationToCell(listToApply,cell);
}
if (ssName=="Stock" && r==2 && c==2){
var company = ss.getRange(r,1);
if(company ==="Unisensor"){
sum(val);
}
}
}
function applyValidationToCell(list,cell){
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
function sum(val){
var res = 0;
var result = ss.getRange(2,3);
for (i =4;i<=10;i++){
var cell = unisensor.getRange(i,3).getValue();
var cellres = unisensor.getRange(i,9).getValue();
if( cell == val){
res+= cellres;
}
}
result.clearContent();
result.setValue(res);
Logger.log(res);
}
'''
Thanks in advance.
If when(e) is being executed without errors, but sum() is not called, this means that your if condition is not fulfilled
Troubleshoot by implementing the following logs:
function When(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var ssName = activeCell.getSheet().getName();
if (ssName=="Stock" && r==2 && c==1){
ss.getRange(r,2).clearContent();
ss.getRange(r,2).clearDataValidations();
var filteredOptions = options1.filter(function(o){return o[0] === val});
var listToApply = filteredOptions.map(function(o){return o[1]});
console.log(listToApply);
var cell = ss.getRange(r,2);
applyValidationToCell(listToApply,cell);
}
if (ssName=="Stock" && r==2 && c==2){
console.log('if (ssName=="Stock" && r==2 && c==2)')
var company = ss.getRange(r,1);
if(company ==="Unisensor"){
console.log('company ==="Unisensor"')
sum(val);
}
else{
console.log('NOT company ==="Unisensor"')
}
}
else{
console.log('NOT if (ssName=="Stock" && r==2 && c==2)')
}
}
After the funciton executes on onEdit trigger - view the logs.
This question already has an answer here:
Merging or Combining two onEdit trigger functions
(1 answer)
Closed 2 years ago.
I made a dependent drop-down with a script.
I'd like to apply it to two sheets.
but only OnEdit1 works and onEdit2 doesn't work.
I don't know what to do with both sheets.
Do I have to add or do something to make two sheets work?
function onEdit(e){
oneEdit1(e);
var tabLists = "lists";
var tabValidation = "Question_B";
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 4 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(0, 1).clearContent().clearDataValidations();
var makes = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) + 1;
if(makeIndex != 0){
var validationRange = datass.getRange(3, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
oneEdit2(e);
var tabListss = "lists2";
var tabValidations = "Qeustion_R";
var sss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datasss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabListss);
var activeCell = sss.getActiveCell();
if(activeCell.getColumn() == 4 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidations){
activeCell.offset(0, 1).clearContent().clearDataValidations();
var makess = datasss.getRange(1, 1, 1, datasss.getLastColumn()).getValues();
var makeIndexs = makess[0].indexOf(activeCell.getValue()) + 1;
if(makeIndex != 0){
var validationRange = datasss.getRange(3, makeIndexs, datasss.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
Two combine two onEdit functions in one:
define both of them outside of the main function
call them from the main function
Sample:
function onEdit(e){
oneEdit1(e);
oneEdit2(e)
}
function oneEdit1(e){
...
}
function oneEdit2(e){
...
}
Below is a script that allows me to combine five different onEdit functions into a single function.
function onEdit(e){
if (e.range.getA1Notation() != "H12" || e.value != "submitResponse") return;
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var m = sh.getSheetByName("Master");
ss.getRange(2,1,ss.getLastRow(),3).copyTo(m.getRange(m.getLastRow()+1,1,ss.getLastRow(),3));
ss.getRange('H12').clearContent();
e.range.clearContents();
}
I'm now hoping to make this script more flexible. See Sheet1 of my spreadsheet: https://docs.google.com/spreadsheets/d/1EoOIQxWyKWOvtlCrmJNI76FAxGhzgXrE4s0F05tw2MY/edit#gid=0
It would be great if instead of limiting myself to H12, I could use the entire column of H:H to submit each corresponding row. So when I change H2 to submitResponse, it copies/pastes A2:G2 to the last row of the Master. When I change H3 to submitResponse, it copies/pastes A3:G3 to the last row of the Master. And so forth.
I tried my hand at this but no luck on execution. I guess I'm missing something.
​function onEdit(e) {
var ss = e.source;
var s = ss.getActiveSheet();
var r = e.range;
var actionCol = 8;
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
var colNumber = s.getLastColumn()-1;
if (e.value == "submitResponse" && colIndex == actionCol) ;
var sourceRange = s.getRange(rowIndex, 1, 1, colNumber);
var targetRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Master").getRange(getLastRow()+1, 1, 1, colNumber);
sourceRange.copyTo(targetRange,SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
Try this:
function onEdit(e){
var sh=e.range.getSheet();
if (sh.getName()=='Sheet1' && e.range.columnStart==8 && e.range.rowStart>1 && e.value=="submitResponse") {
var msh=e.source.getSheetByName("Master");
msh.appendRow(sh.getRange(e.range.rowStart,1,1,7).getDisplayValues()[0]);
}
}
function onEdit(e){
var sh=e.range.getSheet();
if (sh.getName()=='Sheet1' && e.range.columnStart==8 && e.range.rowStart>1 && e.value=="Yes") {
e.range.setValue('');
var msh=e.source.getSheetByName("Master");
msh.appendRow(sh.getRange(e.range.rowStart,1,1,7).getDisplayValues()[0]);
}
}