google sheets script - select multiple a1 notation range - google-apps-script

I'm opting for batch update of row colors using google apps script. However I cannot go for the usual range function, as the rows to be colored are not consecutive. So I thought, a1 notation would be helpful but unfortunately it looks like I can only pass one range of a1 notation and not multiple:
var a1Notations="A1:C1,A3:C3,A10,C10";
sheet.getRange(a1Notations).setBackground("red");
But I'm getting "Range not found" error.
Any ideas how can I make this work?
Thanks!

To multi-select a number of ranges and change the color:
var sheet = SpreadsheetApp.getActiveSheet();
var rangeList = sheet.getRangeList(['A1:C1','A3:C3','A10','C10']);
sheet.setActiveRangeList(rangeList).setBackground("red");
If you want to type a list in a dialog box:
function promptRangesList() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Comma-separated ranges list', 'E.g.: A1:C1,A3:C3,A10,C10 or C3,C7,C17,C56', ui.ButtonSet.OK_CANCEL);
Logger.log(response.getSelectedButton());
// Process the user's response.
if (response.getSelectedButton() == ui.Button.OK) {
if (response.getResponseText()!=='') {
var list = response.getResponseText().split(',');
Logger.log(list);
var rangeslist = SpreadsheetApp.getActiveSheet().getRangeList(list).setBackground("red");
Logger.log(rangeslist);
rangeslist.activate();
} else {
Logger.log('getResponseText empty');
}
} else if (response.getSelectedButton() == ui.Button.CANCEL) {
Logger.log('CANCELED');
} else {
Logger.log('The user clicked the close button in the dialog\'s title bar.');
}
}
Reference:
Class RangeList - Selects the list of Range instances
Class Sheet - Sets the specified list of ranges as the active ranges in the active sheet.
Prompt dialogs - A prompt is a pre-built dialog box that opens inside a Google Docs, Sheets, or Forms editor.

Put the range notations into an array, then loop through the array:
function setMultiRanges() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var a1Notations=["A1:C1","A3:C3","A10","C10"];
var i=0,
arryLngth = a1Notations.length;
for (i=0;i<arryLngth;i+=1) {
//Logger.log(a1Notations[i]);
//Logger.log(typeof a1Notations[i]);
sh.getRange(a1Notations[i]).setBackground("red");
};
};

Set each range independently
sheet.getRange("A1:C1").setBackground("red");
sheet.getRange("A3:C3").setBackground("red");
sheet.getRange("A10:C10").setBackground("red");

According to the doc of Google Sheets' API V4 the way to select multiple ranges is to pass an array of ranges.
var ranges = ["'EVERYTHING'!A:A", "'EVERYTHING'!Z:Z"];
// I use Node.js, so my call to the API looks like this:
service.spreadsheets.values.batchGet({
spreadsheetId: spreadsheetId,
ranges: ranges
}, function(err, result) { ...
Then what you get is an array of data, [{first range}, {second range}, {etc...}]

Related

Openbyurl not working for different tabs in the same sheet

I have a spreadsheet with two different tabs in it. When I pass the link of the second tab, the below code always brings the content of the first tab in the sheet. New to appscript programming, any leads would be appreciated!
const currentSheetName = sheet.getSheetName();
if (currentSheetName == "AAA")
{
var sheetData = sheet.getRange('A1:A50').getValues();
for(var input=1;input<sheetData.length;input++)
{
var newURL = sheetData[input][0];
var cellrow = input+1;
if (newURL != "")
{
try{
var url = sheet.getRange(cellrow,1).getRichTextValue().getLinkUrl();
if (url != "")
{
var dataSheet = SpreadsheetApp.openByUrl(url);
}
else{
var dataSheet = SpreadsheetApp.openByUrl(newURL);
}
var job = dataSheet.getRange('M9').getValue();
}
}
}
openByUrl() returns a Spreadsheet, not a Sheet. You can use getSheetByName() or similar to get the actual sheet.
Structure of the Spreadsheet service
In general the structure of the data in the Apps Script Spreadsheet service is:
SpreadsheetApp > Spreadsheet > Sheet > Range > Data
Which is like:
Service > File > Sheet > Selection > Data
Hence the regular snippet you see at the beginning of many scripts:
ss = SpreadsheetApp.openById("<ID>")
sh = ss.getSheetByName("<SHEET NAME>")
rng = sh.getRange("<RANGE>")
data = rng.getValues()
Though as you have found, calling getRange on a Spreadsheet object does actually return a range. It will usually default to the first sheet in the spreadsheet. Though it is advisable to always explicitly assign the sheet, even if the spreadsheet only contains one sheet.
References
Services and methods of:
SpreadsheetApp service
spreadsheet object
sheet object
range object

Google Sheets Script - Reference a specific cell in a row

I have a sheet where when I change a specific cell to "YES", I need a template sheet to be copied to a new version and named as per the value of a cell on the current row.
I'm having trouble working out how to get the value of the first cell in the row selected. This is what I have so far (I know this is wrong):
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = sheet.getCurrentCell();
if (currentCell = "YES")
{
SpreadsheetApp.getActiveSpreadsheet().toast("New change control sheet added to workbook.","Change Control",15);
var sourceRow = ss.getActiveRange().getRowIndex();
var tabName = ss.getRange(cell,1).getValues();
ss.getSheetByName("CCTemplate").showSheet()
.activate();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.duplicateActiveSheet();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.getActiveSheet().hideSheet();
ss.setActiveSheet(ss.getSheetByName('Copy of CCTemplate'), true);
ss.getActiveSheet().setName("CC" & tabName);
}
}
Any ideas?
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()=='Your Sheet Name' && e.value=="YES") {
e.source.toast="New change control sheet added to workbook.","Change Control",15);
var tabName=sh.getRange(e.range.rowStart,1).getValue();
var tsh=e.source.getSheetByName('CCTemplate');
var csh=tsh.copyTo(e.source);
csh.setName('CC'+tabName);
}
}
You should avoid using activate in your scripts especially in simple triggers where you have to finish in 30 seconds. I think this code does the same thing that you intended for your code. One significant difference is that I use the information that comes in the event object that comes with the trigger. You should add the code Logger.log(JSON.stringify(e)) and then look at the logs you will see that there is a lot of information available to you which removes the need to run extra functions to get things like a spreadsheet.
Use event objects
onEdit offers among others the event objects range and value which are helpful to retrieve the range that has been edited and its value.
Also
When you want to a cell and compare it against a value, like in if (currentCell = "YES") - you need to retrive its value (either currentCell.getValue() or just event.value) and you need to use == instead of = for comparison.
Be careful with getValues() vs getValue(). The former gives you a 2D array and is not necessary if you want to retrieve the value of a single cell.
There is no need to set your sheet to active in order to change its name.
You can rewrite your code as following:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = event.range;
var value = event.value;
if (value == "YES")
{
...
var sourceRow = range.getRowIndex();
var tabName = ss.getRange(sourceRow, 1).getValue();
...
ss.getSheetByName('Copy of CCTemplate').setName("CC" + tabName);
}
}

Stand Alone SpreadsheetApp.getActiveSheet() always return the first sheet

SpreadsheetApp.getActiveSheet() will always return the first sheet rather than the active sheet which is most likely a bug in new sheets.
function testActiveSheet(){
var ssCentral = SpreadsheetApp.openById("xxxxxxxxxxx");
var ssCentralName = ssCentral.getName();
SpreadsheetApp.setActiveSpreadsheet(ssCentral);
Logger.log('the ssCentralName is ' + ssCentralName);
var currentActiveSheet = ssCentral.getActiveSheet();
var currentActiveSheetName = currentActiveSheet.getName();
Logger.log('the currentActiveSheetName is ' + currentActiveSheetName);
}
despite the active sheet is not the first one, it still returns the first one.
If the script is stand-alone and not bound to a specific spreadsheet, an active Sheet would not exist. This is not a bug but expected behaviour. To expand an active sheet is what a user is looking at, not what the script is currently working in; or as said in the official documentation, "the active sheet in a spreadsheet is the sheet that is being displayed in the spreadsheet UI."
If the script is not bound then there is no sheet being displayed to any user and therefore the function just returns the first sheet! Hope that makes sense, let me know if you have any follow-up questions.
See here for details.
in response to #Chris
this is the SCRIPT B
function callActivateNewMember(){
LibraryName.activateNewMember()
}
this is the standalone SCRIPT A
function activateNewMember(){
// DECLARE THE CURRENT ACTIVE WORKSHEET as ssCentral
var ssCentral = SpreadsheetApp.openById("xxxxxxxxxxxxxx");
// check if TeamList sheet is active - if not land on Teamlist and ask to select a
new member by his/her first name
// goto and activate TeamList
var teamListSheet = ssCentral.getSheetByName('TEAM LIST');
var teamListSheetIndex = teamListSheet.getIndex()-1;
Logger.log('the teamListSheetIndex is ' + teamListSheetIndex);
var currentSheet = SpreadsheetApp.getActiveSheet().getName();
Logger.log('the currentSheet is ' + currentSheet);
if(currentSheet !='TEAM LIST'){
ssCentral.setActiveSheet(ssCentral.getSheets(
[teamListSheetIndex]).getRange('B5').activate();
Logger.log('WRONG TABLE');
return;
}
var teamListSheetLastRow = teamListSheet.getLastRow();
var nberRows = teamListSheetLastRow-7
// CHECK IF THE CELL IS IN THE FIRST COL AND IS NOT EMPTY
// ------------------get the current row of the member selected
var MemberRow = ssCentral.getCurrentCell().getRow();
var MemberCol = ssCentral.getCurrentCell().getColumn();
if(MemberRow <8 ||MemberCol>1 ){
Logger.log('RIGHT TABLE - WRONG CELL SELECTED');
return;
}
var memberCellValue = ssCentral.getCurrentCell().getValue();
if(memberCellValue=='' ){
Logger.log('CELL SELECTED EMPTY');
return;
}
// VALIDATION : IS THIS MEMBER WAS ALREADY ACTIVATED
//-----------get the column with “Reporting Activated” as header
var repActiveCol = teamListSheet.getRange("D7").getColumn();
//-----------check if the cell of “Reporting Activated” is Y
var checkY = teamListSheet.getRange(MemberRow, repActiveCol).getValue();
//-----------If checkY = “Y” then Alert OK
if (checkY == "Y") {
Logger.log('MEMBER WAS ALREADY ACTIVATED');
return;
}
// rest of the function
}
after considering the answers from Chris I decided to change the strategy
I now call the function in the standalone SCRIPT along with 2 parameters I catch from the SCRIPT bound to the Spreadsheet: ActiveSheet + ActiveCell.
then I return an error message based on the situation and display alert based on the situation.
it works now but I am not totally satisfied as I wanted to show the most limited code in the SCRIPT bound to the Spreadsheet.
thanks again to Chris for taking the time to explain to me the issue I faced. if you pass by Bali - Indonesia just let me know I'd be glad to have a coffee with you. We have a dev center here.
If your gs script was created from within a Google Sheet you can use
var sheet = SpreadsheetApp.getActiveSheet();
to get the currently selected sheet

How to assign two functions to a single button

I only know how to assign a script to a button in Google Sheets but I don't know how to assign two or three function(scripts) to a single button. That when I press the button in making invoice the invoice change number, saves to next sheet, and print. How could it be.how can i run combine these three scripts at one in google sheets.
function export() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Unwanted Sheet');
sheet.hideSheet();
DriveApp.createFile(ss.getBlob());
sheet.showSheet();
}
function clearRange2() { //replace 'Sheet1' with your actual sheet name
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
sheet.getRange('A10:F26').clearContent();
}
function clearRange2() { //replace 'Sheet1' with your actual sheet name
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
sheet.getRange('A10:F26').clearContent();
}
}
Make another function that calls all the functions you need, then assign that function to the button.
Also if you are calling those functions from the same button, maybe those functions should all be combined into one function?

Auto addition of text within cells of column A if one enters a number in the same cell of the column

I am complete novice in Google script, but have a little bit of knowledge of spreadsheet formula and function.
I need that if my user enter any number in a cell in column A (say, 123 in A1), it automatically gives out in the same cell (as a drop-down list) two options - LAPL/2K17/123 and LA/2K17/123 - to choose from.
I don't think it is possible to achieve through spreadsheet funcions, is it even possible through Google Script as the same cell in which the data is being entered has to have the changed text?
here is an option- not exactly what you are looking for, however it may function for you.
function onEdit(e){
var ss = SpreadsheetApp.getActiveSheet();
var resp = ss.getDataRange().getValues();
for(var n=0;n<resp.length;n++)
{
for(var p=0;p<resp[n].length;p++)
{
if(resp[n][p].toString().match(/^123/)) {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'LAPL/2K17/123?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
ui.alert('Setting Value to LAPL/2K17/123');
ss.getRange(n+1,p+1).setValue('LAPL/2K17/123')
} else {
// User clicked "No" or X in the title bar.
ss.getRange(n+1,p+1).setValue('LA/2K17/123')
ui.alert('Setting Value to LA/2K17/123');
}
};
}
}};