I found this script:
function onEdit () {
var s = SpreadsheetApp.getActiveSheet ();
if (s.getName () == "sheet_name") {
var r = s.getActiveCell ();
if (r.getColumn () == 1) {
var nextCell = r.offset (0, 1);
if (nextCell.getValue () === '')
nextCell.setValue (new Date());
}
}
}
It works if I fill one cell by myself and puts current date to another cell in the right.
BUT if I use Zapier to export my data from Todoist to Google Sheets this script doesn't work. It only works if I change something manually.
Is there any way to make a script which will fill a cell I need with a today date when Zapier export data and fills cells automatically?
Suggestion:
As what Tanaike mentioned, you need to rename your function to something else aside from onEdit() since onEdit is a reserved function name for App Script and use onChange trigger.
But based on how Zapier works, the reason why the current code you have provided is not working is because exports from Zapier is not detected as an active cell, so we would need to revamp the entire code.
Try this instead:
function onZapierUpdate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet2'); //my test sheet is Sheet2
var range = sheet.getRange(2,1,sheet.getLastRow()-1, 2);
var data = range.getValues();
data.forEach(x => x[0] != "" ? x[1] = new Date() : x);
range.setValues(data);
}
After saving the script, set this on an onChange trigger like so:
Now whenever Zapier exports the data, it changes the content of the spreadsheet which means onChange trigger will take effect.
Reference:
https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onChange()
Related
Hi everyone,
I want to copy the data from source sheet to destination sheet. When the data reached the destination sheet, the script able to loop through row 2 in destination sheet to see whether any same ID already existed. If the ID already existed in row 2, then it will overwrite the data in the column, if not, the script will find the last empty column based on row 2 and paste the data there.
So in the screenshot above, since there is no 1004 in destination sheet, then it will paste the data in column E.
This is my code:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSheet ();
var targetfile = SpreadsheetApp.openById("11tpC8SNZ5XB35n7GON0St3ZQ37dIbM8UbXRjmkVAeJQ");
var target_sheet = targetfile.getSheetByName("Sheet1");
var target_range = target_sheet.getRange(3, ss.getLastColumn() + 1);
if (e.range.columnStart == 3 && e.range.rowStart == 16){
if (e.value == 'Submit'){
var source_range = ss.getRange("C4:C14")
source_range.copyTo(target_range);
e.range.clearContent()
}
}
}
My current problems are:
The script is not working when I triggered it in cell C16 (couldn't find the reason)
I'm not sure how to add the checking for ID in destination sheet into my script.
This are my google files
Source sheet: https://docs.google.com/spreadsheets/d/12kKQKT2XSdPkJ46LSV9OiI167NKBdwKkpkOtOf_r_jI/edit#gid=0
Destination sheet:
https://docs.google.com/spreadsheets/d/11tpC8SNZ5XB35n7GON0St3ZQ37dIbM8UbXRjmkVAeJQ/edit#gid=0
Hope to get some advice and help from expert. Any help will be greatly appreciated!
Explanation:
I see a couple problems with the existing script:
Simple Triggers only have authorization on the container Spreadsheet, so to access another Spreadsheet you need to create an Installable Trigger:
// Creates an edit trigger for a spreadsheet identified by ID.
function createTrigger() {
ScriptApp.newTrigger('copyFunction')
.forSpreadsheet('source-sheet-id')
.onEdit()
.create();
}
copyTo() only works when copying to the same Spreadsheet. You would need to use getValues() and setValues() to copy across spreadsheets. The caveat is that formatting is not copied. You would need to manually format the columns if a new column is filled up.
function copyFunction(e) {
var ss = SpreadsheetApp.getActiveSheet();
var targetfile = SpreadsheetApp.openById('dest-sheet-id');
var target_sheet = targetfile.getSheetByName("Sheet1");
var id_table = target_sheet.getRange(2,2,1,target_sheet.getLastColumn()-1).getValues();
if (e.range.getA1Notation() == 'C16') {
if (e.value == 'Submit') {
var source_values = ss.getRange("C3:C14").getValues();
for (i = 0; i < id_table[0][i]; i++) {
if (id_table[0][i] == ss.getRange("C3").getValue())
break;
}
target_sheet.getRange(2,i+2,12).setValues(source_values);
e.range.clearContent();
}
}
}
In google sheets, I am trying to get one data to copy from one sheet to another.
I have this code which is working however I would like it to run onEdit when changing cell E4 in Googlesheet1. I am new at this and doesn't seem to get it to quite work with the solutions I found online.
function ExportRange() {
var destination = SpreadsheetApp.openById('googlesheet1');
var destinationSheet = destination.getActiveSheet();
var destinationCell = destinationSheet.getRange("AC3");
var cellData = '=IMPORTRANGE("https://docs.google.com/spreadsheets/googlesheet2", "AE10:AE9697")';
destinationCell.setValue(cellData);
}
Chose between a simple and installable onEdit trigger, depending on your requirements
For most applciaitons a simple onEdit trigger is sufficient, to use it you just need to rename your function ExportRange() to onEdit()
Take advantage of event objetcs that give you informaiton about the event that fired the trigger
So, the trigger onEdit can give you among others information about the event range - that is the range that has been edited
Now you can implement an if statement to specify that the rest of the funciton shall only be run if the event range and the corresponding sheet are as required
Sample:
function onEdit(event) {
var range = event.range;
var sheet = range.getSheet();
if(range.getA1Notation() == "E4" && sheet.getName() == "Googlesheet1"){
var destination = SpreadsheetApp.openById('googlesheet1');
var destinationSheet = destination.getActiveSheet();
var destinationCell = destinationSheet.getRange("AC3");
var cellData = '=IMPORTRANGE("https://docs.google.com/spreadsheets/googlesheet2", "AE10:AE9697")';
destinationCell.setValue(cellData);
}
}
Please note that this function can only be fired by the trigger in
case of an edit. If you try to run it manually, it will give you an
error because event (and thus event.range) will be undefined if
the funciton was not called by an edit event.
Using the code below I am able to generate a hyperlink to each page within my googlesheet, with the name of that sheet:
/**
* Gets the Sheet Name of a selected Sheet.
*
* #param {number} option 0 - Current Sheet, 1 All Sheets, 2 Spreadsheet filename
* #return The input multiplied by 2.
* #customfunction
*/
function SHEETNAME(option) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var thisSheet = sheet.getName();
var thisgid = sheet.getSheetId();
//Current option Sheet Name
if(option === 0){
return thisSheet;
//All Sheet Names in Spreadsheet
}else if(option === 1){
var sheet1 = [];
ss.getSheets().forEach(function(val){
sheet1.push(`=HYPERLINK("#gid=${val.getSheetId()}","${val.getName()}")`)
});
return sheet1;
//The Spreadsheet File Name
}else if(option === 2){
return ss.getName();
//Error
}else{
return "#N/A";
};
};
function GETLINK(option) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(option);
if (sheet != null) {
return(sheet.getId());
};
};
This returns a column of appropriate functions in each cells but does not evaluate the function. If I copy the function into another cell it does evaluate?!
=HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1")
I've tried also tried adding HTML but this also becomes a string and is not evaluated
sheet1.push(`${val.getName()})
When the custom formula of =SHEETNAME(0) is put to a cell, you want to put the sheet name of the active sheet on the Spreadsheet.
When the custom formula of =SHEETNAME(1) is put to a cell, you want to put the formulas of =HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1") for all sheets.
When the custom formula of =SHEETNAME(2) is put to a cell, you want to put the Spreadsheet name of the active Spreadsheet.
If my understanding is correct, how about this answer?
Issue and workaround:
Unfortunately, in the current specification of Google side, the formula cannot be put to a cell by the custom formula. When the formula is put to a cell by the custom formula, the formula is put as the string value. I think that this is the reason of your issue.
As a workaround, I would like to propose to put the formula of =HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1") using the OnEdit event trigger when the custom formula of =SHEETNAME(#) is put to a cell. I think that by this workaround, your goal can be achieved.
When your script is modified, please modify as follows.
Modified script:
In this case, the simple trigger can be used. So please copy and paste the following script to the script editor, and save the script. In order to use this script, as a test, please put =SHEETNAME(1) to a cell.
function onEdit(e) {
const spreadsheet = e.source;
const range = e.range;
const sheet = range.getSheet();
const formula = range.getFormula();
const f = formula.match(/\=SHEETNAME\((\d+)\)/i);
if (f && f.length > 0) {
if (f[1] == "0") {
range.setValue(sheet.getSheetName());
} else if (f[1] == "1") {
var formulas = spreadsheet.getSheets().map(val => [`=HYPERLINK("#gid=${val.getSheetId()}","${val.getName()}")`]);
range.offset(0, 0, formulas.length, 1).setFormulas(formulas);
} else if (f[1] == "2") {
range.setValue(spreadsheet.getName());
}
}
}
In this case, there is the function of SHEETNAME() in your script editor. So when =SHEETNAME(1) is put to a cell, at first, the function of SHEETNAME() is run, and then, the function of onEdit is automatically run by the OnEdit event trigger. If you don't want to show the values from SHEETNAME(), please replace the function of SHEETNAME() as follows.
const SHEETNAME = () => "";
Note:
In this case, when you directly run the function of onEdit at the script editor, an error occurs. Please be careful this.
This script is run under V8.
References:
Simple Triggers
Event Objects
setFormulas()
In google apps script, with onEdit trigger, how do I get the old values of all the cells if the edited range has multiple cells? In the Event object documentation https://developers.google.com/apps-script/guides/triggers/events it is specified that the oldValue attribute is "Only available if the edited range is a single cell".
In my case, I use onEdit from the Event object to run a function (that needs oldValue and newValue) only when a specific column is edited. It works fine when the user selects only one cell in my specific column, but if the user selects a few cells or the entire row for example, only data from the first selected cell is retrieved but I need to access the oldValue of my specific column.
You want to retrieve old values when the multiple cells are edited.
If my understanding is correct, how about this answer?
Issue:
Unfortunately, in the current stage, when the multiple cells are edited, there are no methods for retrieving all old values from the event object.
Workaround:
So as the current workaround, how about this flow?
Copy the active Spreadsheet.
This is run only one time.
When the cells are edited, the old values are retrieved by comparing the active Spreadsheet and copied Spreadsheet.
Update the copied Spreadsheet.
By above flow, the cycle for retrieving old values when the cells are edited can be created. When this flow is reflected to the script, it becomes as follows. Please think of this as just one of several answers.
Sample script:
When you use this script, please install the OnEdit event trigger to the function of onEditByTrigger() after copy and paste this script to the script editor of the container-bound script. By this, when the cells are edited, you can see the current and old values at the log.
var backupfilename = "backupfile";
function copyToo(srcrange, dstrange) {
var dstSS = dstrange.getSheet().getParent();
var copiedsheet = srcrange.getSheet().copyTo(dstSS);
copiedsheet.getRange(srcrange.getA1Notation()).copyTo(dstrange);
dstSS.deleteSheet(copiedsheet);
}
// This is run only one time.
function init() {
// Source
var srcss = SpreadsheetApp.getActiveSheet();
var range = srcss.getDataRange().getA1Notation();
var srcrange = srcss.getRange(range);
var srcsheetname = srcss.getName();
// Destination
var backupfile = DriveApp.getFilesByName(backupfilename);
var dstid = backupfile.hasNext()
? backupfile.next().getId()
: SpreadsheetApp.create(backupfilename).getId();
var dstss = SpreadsheetApp.openById(dstid).getSheets()[0]
var dstrange = dstss.getRange(range);
dstss.setName(srcsheetname);
copyToo(srcrange, dstrange);
PropertiesService.getScriptProperties().setProperty('backupfileid', dstid);
return dstid;
}
function onEditByTrigger(e) {
var columnNumber = 1; // If you want to retrieve the old values when the column "A" is edited, it's 1.
var source = e.source;
var range = e.range;
var dstid = PropertiesService.getScriptProperties().getProperty('backupfileid');
if (!dstid) {
dstid = init();
}
if (e.range.columnStart == columnNumber) {
var range = source.getSheetName() + "!" + range.getA1Notation();
var fields = "sheets(data(rowData(values(formattedValue,userEnteredFormat,userEnteredValue))))";
var currentValue = source.getRange(range).getValues();
var oldValue = SpreadsheetApp.openById(dstid).getRange(range).getValues();
Logger.log("currentValue %s", currentValue)
Logger.log("oldValue %s", oldValue)
}
// Update backup file
var range = e.source.getDataRange().getA1Notation();
var srcrange = e.source.getRange(range);
var dstrange = SpreadsheetApp.openById(dstid).getSheets()[0].getRange(range);
copyToo(srcrange, dstrange);
}
Note:
This is a sample script for retrieving the old values when the multiple cells were edited. So if you use this script, please modify this to your situation.
References:
Event Objects
Installable Triggers
If this was not the direction you want, I apologize.
I have a simple function to get some cell value
function getValue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[4];
var range = sheet.getRange("C2:C4");
var cell = range.getCell(1, 1); // "C2"
if (cell.isBlank()) {
return 'error'
} else {
return cell.getValue()
}
}
But when I change data in C2, cell, which contains =getValue() function does not refresh itself instantly. Only if I run script again and get back to sheet. Is it possible to speed this process up? Any code for this? Thanks.
If you have to use the custom functions for this situation, how about this workaround? I don't know whether this is the best way for you. Please think of this as one of several answers.
The flow of script is as follows.
Flow :
Retrieve all values and formulas on the sheet.
Remove values of cells which have formulas.
Reflect values to the sheet using SpreadsheetApp.flush().
Import formulas to the removed cells.
By onEdit(), when you edit the cell, this sample script is launched.
Sample script :
function onEdit(e) {
var range = e.source.getDataRange();
var data = range.getValues();
var formulas = range.getFormulas();
var values = data.map(function(e){return e.slice()});
for (var i in formulas) {
for (var j in formulas[i]) {
if (formulas[i][j]) {
data[i][j] = formulas[i][j];
values[i][j] = "";
}
}
}
range.setValues(values);
SpreadsheetApp.flush();
range.setValues(data);
}
Note :
In this situation which imports a value at "C2" to the cell at =getValue(), the refresh speed is slower than that of #random-parts's method.
To use onEdit() is also proposed from #Cooper.
If this was not useful for you, I'm sorry.