Rename a Google sheet tab based on cell - google-apps-script

I partially know the name of 2 sheets I need to rename when the cell B2 change in each sheet:
One in called csv_nat and it should be renamed to csv_nat_30_11_2019 when cell B2 in sheet changes (it is a date format like 30/11/2019)
The other one has the exact same structure and is called csv_alt
My main issue is the following. When the tab name has already changed, I need to locate the sheet from its root name (csv_nat) and I have not figured out how.
My limited and starting code for now is:
function onEdit(e){
if(e.range.getA1Notation() != "B2") return;
e.source.getActiveSheet().setName("csv_nat_"+e.value);
}
Besides, does the onEdit function work for any change or only when user edits the cell?

If you want to locate your csv_nat or csv_alt sheets, you can use regex in this case.
Try this example
if sheet contains csv_nat or csv_alt it will alert you:
var ss = SpreadsheetApp.getActiveSheet();
if(ss.getSheetName().match(/csv_nat|csv_alt/)){
SpreadsheetApp.getUi().alert("Working Sheet is found")
}
Reference
String.match()

Related

How to make a trigger that run when there is changes on specific column of different Spreadsheet

I want to ask about how to make a trigger that run when there are changes on different spreadsheet (Source spreadsheet) and on specific column.
I have 2 different spreadsheet (Source Spreadsheet and Target spreadsheet) and the trigger will working on target spreadsheet when there are changes that happened in the specific column of the source spreadsheet. I've just made the code, but it's not working. This code was made on the target spreadsheet. I've already use the onEdit() and onChange() trigger, but nothing happen or run this script.
This code still show some error like:
TypeError: e.source.openById is not a function
Here is the code that I've been made on the target spreadsheet:
function inChange(e) {
var source_spreadsheet_sheetName = e.source.openById('').getName(); //ID of source spreadsheet
var Row = e.source.getActiveCell().getRow();
var Column = e.source.getActiveCell().getColumn();
if (source_spreadsheet_sheetName == '' && Row >= 2 && Column == 2) { //the name of sheet from source spreadsheet and the specific column that has changes on it.
myFunction(); //It will run the process of this myFunction script that I've been made on target spreadsheet
}
}
Your trigger runs. But it fails on first line of code because you're calling a method that you cannot.
Several problems.
Problem #1
var source_spreadsheet_sheetName = e.source.openById('').getName();
the openById probably does not work because source is already of type Spreadsheet so you can directly call getName on it.
It should be
var source_spreadsheet_sheetName = e.source.getName();
Problem #2
There is no getActiveCell on a Spreadsheet, only on a Sheet.
// if you know the sheet name in advance
let sheet = e.source.getSheetByName(YOUR_SHEET_NAME)
// or if you don't, get the active sheet (and don't use the line before)
let sheet = e.source.getActiveSheet()
let row = sheet.getActiveCell().getRow();
let column = sheet.getActiveCell().getColumn();
Problem #3
if (source_spreadsheet_sheetName == ''
A sheet name cannot be empty so this will never be true
source_spreadsheet == '' cannot be true because it's the name of a spreadsheet

Is there a getter for an entire worksheet or range including formulas, values, merges, formatting etc. in Apps Script?

I'm trying to make a backup of an entire worksheet, including formulas, values, formatting, row and column size, cell merges, etc. so that when a user is finished editing I can reset the sheet. Currently I'm using a Range.getFormulas() to create a stringified object (that I can then paste into my code as a constant) to reset all of the content of the cells, but if the user changes the row size or deletes a cell, I'd like to be able to quickly rebuild the entire sheet without iterating through rows and columns (Apps Script is too slow for that). My previous method was to create a duplicate of the sheet and simply hide it, but someone can still unhide and edit that.
I've been digging through the documentation, but I haven't found anything useful. To sum up, I'd like to have something like this:
function resetHandler() {
var destinationWorksheet = SpreadsheetApp.getActiveSpreadsheet().getRange("A1:H132"),
backupWorksheet = [...object...];
backupWorksheet.copyTo(destinationWorksheet);
}
where "[...object...]" is the output of a getter that contains the entire sheet as an object. I tried JSON.stringify(SpreadsheetApp.getActive().getSpreadSheetByName("Workorder").getRange("A1:H132")) but it just outputs "{}" since the Range class is all private.
If there isn't a way, I can always fall back on a hidden backup sheet.
Description
I've created an example where a sheet has formulas, conditional format and data validation. Using testCopyTo I copy Sheet1 to a Backup spreadsheet. All attributes are copied. Using testCopyFrom first copy from the Backup spreadsheet then copy the range within the spreadsheet and finally delete the copy of the backup sheet.
Before restore
After restore
Script
function testCopyTo() {
try {
let source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
let dest = SpreadsheetApp.openById("xxxx.......");
source.copyTo(dest);
}
catch(err) {
console.log(err);
}
}
function testCopyFrom() {
try {
let dest = SpreadsheetApp.getActiveSpreadsheet();
let source = SpreadsheetApp.openById("xxxx.......").getSheetByName("Copy of Sheet1");
source.copyTo(dest);
source = dest.getSheetByName("Copy of Copy of Sheet1");
dest = dest.getSheetByName("Sheet1");
source.getDataRange().copyTo(dest.getRange(dest.getLastRow()+1,1));
SpreadsheetApp.getActiveSpreadsheet().deleteSheet(source);
}
catch(err) {
console.log(err);
}
}
There is no Apps Script method to retrieve a spreadsheet by its name, you need to retrieve it by id or url
Keep in ind that in Google Drive, multiple spreadsheets with the same name can exist within a folder, this is why it would not make any sense to try and retrieve a spreadsheet by its name
Instead, you need to use the method openById(id) or openByUrl(url) (less recommended)
To get a sheet from an external spreadsheet and copy it to the current one, you can do the following:
function resetHandler() {
var destinationWorksheet = SpreadsheetApp.getActiveSpreadsheet();
backupWorksheet = SpreadsheetApp.openById("123456789").getSheetByName("The Name of the Sheet");
backupWorksheet.copyTo(destinationWorksheet);
}
where 123456789 is the id of the backup spreadsheet which you can find among others in the url in the address bar, which looks something like https://docs.google.com/spreadsheets/d/1234456789/edit#gid=1392862199
To copying a range from a sheet from an external spreadsheet into a sheet in the curent spreadsheet with copyTo is not possible, you can only use getValues() and setValues respectively, however this will not copy the formatting. Also for the formulas, you need to perform additoinally getFormulas() and setFormulas(formulas).
UPDATE
If you would like to copy a range from one sheet into another sheet of the same spreadsheet, you can modify your code as following:
function resetHandler() {
var activeSpreadsheet = SpreadsheetApp.getActive();
var destinationSheet = activeSpreadsheet.getSpreadSheetByName("Workorder");
var destinationRange = destinationSheet.getRange("A1:H132");
var backupWorksheet = activeSpreadsheet.getSpreadSheetByName("Backup").getRange("A1:H132");
var backupRange = backupWorksheet.getRange("A1:H132");
backupRange.copyTo(destinationRange);
}
The main problem with your code was that you mixed up the methods copyTo(destination) for ranges with copyTo(spreadsheet).
Both methods have the same name, but the first copies a range into another range (into the same sheet or another sheet of the same spreadsheet; overwriting the previous contents), while the other one inserts a whole (additoinal) sheet from a spreadsheet into the same or different spreadsheet
Have a careful look at the sample in the documentation for appplying the correct syntax in each case
Also: The method getSpreadSheetByName() does not exist, it is getSheetByName()
Careful with the wording: Google talks about sheets and spreadsheets, which are the equivalents of Excel worksheets and workbooks respectively.

Google sheet buttons don't work on mobile

I noticed recently that buttons created in the google worksheet don't work on mobile. I found the resolution, however now I wonder how could I point the code presented in the article to the specific tab in my worksheet, for instance, "sheet 1" and "sheet 2". As I'd like the code to work on both tabs "sheet 1" and "sheet 2", in parallel. So that two different users, can work on two different tabs at the same time.
Would anyone know how to overcome this issue?
https://medium.com/macadamscripts/create-button-in-google-sheets-mobile-2979579025ef#:~:text=Luckily%2C%20there%20is%20a%20workaround,serve%20as%20the%20%E2%80%9Cbutton%E2%80%9D
function onEdit(e) {
if (e.range.getA1Notation() == 'D4') {
if (/^\w+$/.test(e.value)) {
eval(e.value)();
e.range.clear();
}
}
}
Best,
D
Try to use activeSheet.getName in handling/checking a specific sheet on your spreadsheet. See sample code below on how to achieve it:
function onEdit(e) {
//Set a comment on the edited cell to indicate when it is changing.
var range = e.range;
var activeSheet = e.source.getActiveSheet();
if (activeSheet.getName() == "Sheet1") { //Sheet1 is just a sample name of the sheet. Modify it according to the name of your sheet.
range.setNote("Last Modified:" + new Date());
}
}
Reference:
use onedit() trigger on a specific sheet within google scripts for google sheets
Just for anyone else who may read this, Google defines:
Spreadsheet = the actual Sheet Document containing the sub sheets.
Sheet = the "tabs" as Ducatia uses.
Anyway, to answer the question, you could try using this which will set the tab to whatever is specified. You can select a sheet based on index of the array but sheet orders can be moved and unfortunately names can be changed too, but not accidentally like simple sheet reorder, so just need to be careful. You can get the sheet's ID but I can't see a way to select a sheet by ID. If I do I'll update this.
I used something like this:
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName("TEST"));
If you run the above, with a proper name, you can watch your spreadsheet change the UI view to the specified sheet.

Name a sheet based on a cell in that sheet

Im looking to make 60+ copies of a template spreadsheet using the google sheets API. I would like to be able to name each sheet using a cell in that same sheet.
function NewBracket() {
var spreadsheet = SpreadsheetApp.getActive();
var name = spreadsheet.getActiveRange().activate() ;
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Template'), true);
spreadsheet.duplicateActiveSheet();
spreadsheet.getRange('B1').activate();
name.copyTo(spreadsheet.getActiveRange(),SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getActiveSheet().setName(name);
};
When I run this, the sheet is created properly, the cell B1 is named properly, but the newly created sheet is named Range, not the name that is input into cell B1. How do I get the above code to name the sheet the same name that shows up in the B1 cell?
The answer came from user ra89fi in the comments, but it worked. I changed spreadsheet.getActiveSheet().setName(name); to spreadsheet.getActiveSheet().setName(name.getValue()); and it worked perfectly.

Google Apps Script - Spreadsheet run script onEdit?

I have a script that I use in my spreadsheet. It is at cell B2 and it takes an argument like so =myfunction(A2:A12). Internally the function gets info from a large range of cells.
Nothing seems to work. I tried adding it to
Scripts > Resources > Current Project Triggers > On edit
Scripts > Resources > Current Project Triggers > On open
How can I have this function update the result with every document edit?
When you are making calls to Google Apps services inside your custom function (like getRange and getValues etc), unfortunately there is no way of updating such custom functions with each edit, other than passing all of the cells that you are "watching" for editing.
And, perhaps even more frustratingly, the workaround of passing say a single cell that references all of your "watched" cells with a formula doesn't trigger an update - it seems that one needs to reference the "watched" cells directly.
You could pass GoogleClock() as an argument which will at least update the function output every minute.
But the advice from many members on this forum (who have much more knowledge about this stuff than me) would simply be: "don't use custom functions".
I am not sure if this exact code will work but you can try something like this...
function onEdit(event){
var activeSheet = event.source.getActiveSheet();
if(activeSheet.getName() == "ActiveSheetName") {
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("TargetSheetName");
targetSheet.getRange(2,2).setValue("=myfunction(A2:A12)");
}
}
Assuming that B2 cell in on the sheet "TargetSheetName" and assuming that the edited cell is on the sheet "ActiveSheetName", the function onEdit will trigger when you edited any cell in any sheet. Since there is an if statement to check if that edited cell is on the sheet "ActiveSheetName" it will trigger only if the edited cell is on that sheet and it will set the B" cell to the value =myfunction(A2:A12), forcing it to update (i guess).
hope that i am correct and that i was helpful
I had a similar issue, for me I wanted to "watch" one particular cell to trigger my function.
I did the following (pretending A1 is the cell i am watching)
IF(LEN(A1) < 1, " ", customFunction() )
This successfully triggered if I ever edited that cell. However:
"Custom functions return values, but they cannot set values outside
the cells they are in. In most circumstances, a custom function in
cell A1 cannot modify cell A5. However, if a custom function returns a
double array, the results overflow the cell containing the function
and fill the cells below and to the right of the cell containing the
custom function. You can test this with a custom function containing
return [[1,2],[3,4]];."
from: https://developers.google.com/apps-script/execution_custom_functions
which makes it almost useless, but it might work for your case?
If the custom function is assigned to a project trigger it has more power so personally I ended adding it to "Scripts > Resources > Current Project Triggers > On edit"
and basically "watched a column" so it only did things if the current cell was within the "edit range". This is a bit of a bodge and requires some hidden cells, but it works for me at the moment
var rowIndex = thisspreadsheet.getActiveCell().getRowIndex();
var colIndex = thisspreadsheet.getActiveCell().getColumn();
//clamp to custom range
if(rowIndex < 3 && colIndex != 5)
{
return 0;
}
//check against copy
var name = SpreadsheetApp.getActiveSheet().getRange(rowIndex, 5).getValue();
var copy = SpreadsheetApp.getActiveSheet().getRange(rowIndex, 6).getValue();
if(name != copy )
{
///value has been changed, do stuff
SpreadsheetApp.getActiveSheet().getRange(rowIndex, 6).setValue(name);
}