Trigger when change is happening in specific sheet - google-apps-script

I have a sheet with data that is imported through IMPORTRANGE to another sheet. When I make changes to Spreadsheet 1 >>> Spreadsheet 2 I have a script that copy the data from Copy to Paste. It is working all fine however I want to make sure that the script only runs when changes are made in the 'Copy' sheet and not any other. At the moment it runs independent on what sheet I make changes in.
Spreadsheet 1
Spreadsheet2
I tried onChange trigger two different ways...
This one makes the change but triggers when changes are made in any of the sheets
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getName() === 'Copy') {
copyInfo();
}
}
AND
(this does not work)
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
if( sheet.getActiveSheet().getName() !== "Copy" ) return;{
copyInfo();
}}
The copy code looks like this...
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
// get source range
var source = copySheet.getRange(2,1,12,8);
// get destination range
var destination = pasteSheet.getRange(2,1,12,8);
// copy values to destination range
source.copyTo(destination);
}

Copy Changes from one Sheet To Another after Imported Range Changes
Spreadsheet 2 Imports a range to Sheet1 from Spreadsheet 1 Sheet1 and when a change occurs in Spreadsheet 2 Sheet1 we copy data from Spreadsheet2 Sheet1 to Spreadsheet2 Sheet 2.
function onMyChange(e) {
//Logger.log(JSON.stringify(e));//useful during setup
//e.source.toast("Entry");//useful during setup
const sh = e.source.getSheetByName("Sheet1")
Logger.log(sh.getName());
if(e.changeType == "OTHER") {
let tsh = e.source.getSheetByName("Sheet2");
sh.getRange(2,1,12,8).copyTo(tsh.getRange(2,1));//Only need the upper left hand corner of the range. Thus you change change the size of the source data without having to change the target range.
}
}
One might be inclined to attempt to use e.source.getActiveSheet() unfortunately this does not necessary get the sheet that you have written unless in is SpreadsheetApp.getActive().getSheets()[0]. onChange event object always returns the most left sheet in the spreadsheet in this situation so it can't be used to identify the sheet that is being written to from the importRange function.
With this function you no longer require another function.

A simple if should work, but you're trying to compare the name to the wrong field. According to Google's documentation, the source field in the Event Object e returns the Spreadsheet object, which is the entire document.
Instead you can use the e.range field, which contains the exact range where the edit was made, and the Range class has a getSheet() method to get the parent Sheet.
So you can modify your sample to the following:
function Trigger2(e) {
var sheet = e.range.getSheet();
if (sheet.getName() === 'Copy') {//Edit: this will only work if sheet is the most left sheet in the spreadsheet
copyInfo();
}
}

Related

Google Sheets Apps Script: Copy selected Cells into new Worksheet

I'm new to Apps Script and don't know how I can get my selected cells copied into another worksheet.
I want the following but instead of copying A7:A9 into a new worksheet i want the cells/range that are selected at the time to be copied. the selected cells/range are going to be different each time that i want to use the script.
var rangeList = SpreadsheetApp.getActiveRangeList();
function TestMakro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A7:A9').activate();
spreadsheet.insertSheet(1);
spreadsheet.getRange('Tabellenblatt1!A7:A9').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
};
thanks
So instead of using SpreadsheetApp.getActive() it's better to define separately the spreadsheet and the active range. you can then get A1 annotation of the active range to find the same area in the new sheet (I assumed that is what you are trying to achieve). Key here is that even if you create a new sheet, it doesn't become Active. Hence you still need to find the same range in it.
Here is the code:
function TestMakro() {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const activeRange = spreadsheet.getActiveRange();
const newSheet = spreadsheet.insertSheet(1)
activeRange.copyTo(newSheet.getRange(activeRange.getA1Notation()) , SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false)
}

Trying to build G Sheets scrip that will copy and paste a dynamic range within same spreadsheet

I'm trying to build a script that will allow me to copy and value paste a range from tab "robert" to tab "brendan". However, the range in "robert" is dynamic, so sometimes it will need to copy and paste A1:M3 or A1:M20. I'd also like it to append to the the first rows of the destination. So when run, the script looks for values on the source tab (robert), copies them, and pastes their values at the top of the destination (brendan) without overwriting existing data.
So far I've worked with the below script from a different thread:
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange('Robert!A1:M100');
source.copyTo(ss.getRange('Brendan!A4'), {contentsOnly: true});
source.clear();
}
The problem here is the script range is static, and it overwrites anything in the destination. I do like the clearing of the source though and the values paste. Any help would be greatly appreciated!
Define the range using variables. Use getLastRow() and getLastColumn() Instead of hard-coding it.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var robWs = ss.getSheetByName("Robert");
var brenWs = ss.getSheetByName("Brendan");
var robLr = robWs.getLastRow()
var robLc = robWs.getLastColumn()
//range starts at row 1 and column 1 (A1), you may need to change accordingly
var vals = robWs.getRange(1,1,lr,lc).getValues;
//paste range also starts at A1. you can find the lr on this sheet and start the range there
brenWs.getRange(1,1,lr,lc).setValues(vals);
function moveValuesOnly() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Robert');
var rg=sh.getActiveRange();//copies active range for source
var vA=rg.getValues();
dsh=ss.getSheetByName('Brendan');
dsr=4;//destination start row
dsc=1;//destination start col
dsa=2;//destination number of rows to append to
drg=dsh.getRange(dsr,dsc,rg.getHeight(),rg.getWidth());
var dvA=drg.getValues();
vA.forEach(function(r,i){
if(i<dsa){
r.forEach(function(c,j){
//only write data in destination for these rows if destination value is null
if(dvA[i][j]='') {
dvA[i][j]=c;
}
});
}else{
dvA[i][j]=c;
}
})
drg.setValues(dvA);
}

Can I protect the name of a Google sheet? Or reference the sheet by position rather than name?

I manage a Google spreadsheet that has multiple sheets. I use scripts to automate some processes on the first sheet. Recently, a user accidentally renamed the first sheet -so the script stopped working until I found out and corrected the name of the sheet.
My question is: how can I protect against this happening again? Is there a way to protect the sheet name from being changed by users other than myself? Or, in the script, is there a different way to reference the first sheet, regardless of its name? I.e., instead of sheet.getSheetName is there something like sheet.getFirst that returns the first sheet, regardless of its name?
Here's a sample of one script that is being affected.
function onOpen(){
//Sheet where this script should run
var SHEETNAME = "Scan IN Check OUT"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (sheet.getSheetName() == SHEETNAME ) { //Check we are on the correct sheet
var sheet = SpreadsheetApp.getActiveSheet();
//Find the last cell with data in that specific column (A in this case)
var lastCell = sheet.getRange('A1').getNextDataCell(SpreadsheetApp.Direction.DOWN);
//Activate the next cell
lastCell.offset(1, 0).activate();
}
}
SpreadsheetApp.getActive().getSheets()[0]; will always be the first sheet on the left which may not always be the same sheet since users can change their position. You can use the first function below to see how the indexes change as you move sheets around. WARNING: don't do this in a spreadsheet that contains important information because it first clears the sheet and then writes the name, the id's and the indexes for all sheets.
So you can also use the first function to get the id of the sheet that you want not to change.
The second function will get the sheet by id. If you will select the id of your sheet and replace the string 'My Sheet ID' with it and also put the correct name of your sheet where it says my sheet name, then when you runTwo() it will move you sheets all the way to left (ie index 1) and rename to whatever you want.
function runOne() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var sht=ss.getSheets();
sh.clear();
sh.appendRow(['Name','id','index']);
sht.forEach(function(s){
sh.appendRow([s.getName(),s.getSheetId(),s.getIndex()]);
});
}
function getSheetById(id) {
var id=id||'My Sheet ID';
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
for(var i=0;i<shts.length;i++) {
if(shts[i].getSheetId()==id) {
return shts[i];
}
}
}
function runTwo() {
var ss=SpreadsheetApp.getActive();
var sh=getSheetById().activate();
ss.moveActiveSheet(1);
sh.setName('My Sheet Name');
}
Of course the real problem here is that if the user can change the name of your sheet they can also go in and change your script.

How come this function isn't returning anything in Google Sheets?

I'm trying to be able to access information on the active sheet, but I can't even seem to "connect" with the sheet to begin with. I'm just wondering why this small function isn't returning anything.
I've played around with different ways to call the sheet (e.g. getActive(), getActiveSheet(), getSheetByName(), etc.), and changing the name of the sheet itself.
function CLICK (){
var sheetName = SpreadsheetApp.getActiveSheet();
return sheetName;
}
I just want it to return the sheet name so I know it's accessing the sheet properly.
For your var please try:
sheetName = SpreadsheetApp.getActiveSheet().getName();
Hi there are some different options you can choose depending on what you want to do. I would suggest to read https://developers.google.com/apps-script/guides/sheets. Here are the examples I have for you.
function myFunction() {
/* To work on the first sheet sheet of the Spreadsheet, use getName(); to get the name of the spreadsheet*/
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
/* To get the name of the active sheet*/
var sheet2 = SpreadsheetApp.getActiveSheet().getName();
/* To add values on the active cell (current selected cell)*/
var sheet3 = SpreadsheetApp.getActiveRange().setValue(sheet2);
/*To work on the sheet with a specific Name*/
var sheet4 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
console.log(sheet2);
console.log(sheet4);
/*The difference between getSheets()[0] and getSheetByName("name") is that getSheets()[0]
will always work with the first sheet even if you change position and getSheetByName() will
always work with the sheet you named even if you change position, the position is 0 based*/
}
Here is another function to get name, url and id of the srpeadsheet.
function getDetails(){
var getName = SpreadsheetApp.getActiveSpreadsheet().getName();
var getUrl = SpreadsheetApp.getActiveSpreadsheet().getUrl();
var getId = SpreadsheetApp.getActiveSpreadsheet().getId();
console.log(getName);
console.log(getUrl);
console.log(getId);
}
Remember that if you are using a script editor that is attached to the Spreadsheet for sure you will be working on that spreadsheet. Now if you created a new script directly from script.google.com then you will need to first open the spreadsheet by using an Id or a url to open that specific spreadsheet.
I hope this helps.

Why can't I copy ranges between spreadsheets with copyValuesToRange()?

I am able to copy a range of values from one spreadsheet to another with Range.setValues(values) but not with Range.copyValuesToRange(sheet, column, columnEnd, row, rowEnd):
function test()
{
var sourceSheet = SpreadsheetApp.getActiveSheet();
var sourceCols = sourceSheet.getRange("A1:B10");
// Copying the first two columns this way works.
var sheet1 = SpreadsheetApp.create("New Spreadsheet 1").getActiveSheet();
sheet1.getRange("A1:B10").setValues(sourceCols.getValues());
// Copying the first two columns this way does not work.
var sheet2 = SpreadsheetApp.create("New Spreadsheet 2").getActiveSheet();
sourceCols.copyValuesToRange(sheet2, 1, 2, 1, 10);
}
Specifically, after running the code, New Spreadsheet 1 contains range A1:B10 of the source spreadsheet, while New Spreadsheet 2 is empty. Both have a single sheet named "Sheet1". The source spreadsheet is unmodified.
What am I doing wrong? I'm wondering if the failing method only works within a single spreadsheet, but I see nothing in the documentation about that.
The reason I want to use Range.copyValuesToRange() is so I can also copy the formatting with Range.copyFormatToRange().
The methods copyValuesToRange and copyFormatToRange, as well as Range.copyTo, are restricted to copying within the same spreadsheets, although documentation is not explicit about that. A workaround which allows you to copy between spreadsheets with formatting is to copy the entire sheet to the destination spreadsheet, using Sheet.copyTo method. Then copy the required range from there, and optionally, delete the sheet. Like this:
function cc() {
var ss1 = SpreadsheetApp.openByUrl('some url');
var ss2 = SpreadsheetApp.openByUrl('another url');
var sheet1 = ss1.getSheetByName('name');
var sheet2 = ss2.getSheetByName('another name');
// the above was the setup, main code begins here
var copySheet = sheet1.copyTo(ss2);
copySheet.getRange("A1:B2").copyTo(sh2.getRange("C1:D2"));
ss2.deleteSheet(copySheet);
}