Apply conditional formatting - google-apps-script

I have checked multiple threads for the result I am looking for and have not had any luck.
What I am trying to accomplish is change a cells font color and size based on a specific value. (if col B1:B equals "Changes:", change font to red and bold)
I was able to get the following to work:
function onEdit(e) {
if (e) {
var ss = e.source.getActiveSheet();
var r = e.source.getActiveRange();
// If you want to be specific
// do not work in first row
// do not work in other sheets except "Sheet3"
if (r.getRow() != 1 && ss.getName() == "Sheet3") {
// E.g. status column is 2nd (B)
status = ss.getRange(r.getRow(), 2).getValue();
// Specify the range with which You want to highlight
// with some reading of API you can easily modify the range selection properties
// (e.g. to automatically select all columns)
rowRange = ss.getRange(r.getRow(),1,1,19);
// This changes font color
if (status == 'CHANGES:') {
rowRange.setFontColor("#FF0000");
rowRange.setFontWeight("bold");
} else if (status == 'DELETIONS:') {
rowRange.setFontColor("##FF0000");
rowRange.setFontWeight("bold");
// DEFAULT
} else if (status == '') {
rowRange.setFontColor("#000000");
}
}
}
}
The problem with my script is I want it to run on a trigger ("on Open" or "on Change"), not "on Edit". I use a script to copy data from Sheet1 and paste it to Sheet3. After the copy/paste script runs, I want the formatting script to run on Sheet3 and edit the cells per the above.

Related

UPDATE: Hide named ranges (columns) with dropdown in same sheet

I am attempting to create a script for a dropdown selection in cell B2, that when selected for one, will hide columns corresponding two the other choices in the list sheet.
The dropdown in cell B2 is has as its data validation criteria the following list:
"NORMAL," "HARD," "MAX POINTS"
And the following column ranges in the sheet correspond to the selections in parentheses:
Columns D-K ("NORMAL"), Columns L-S ("HARD"), Columns T-AA ("MAX POINTS")
I would like the script to work such that selection of one of the dropdown choices will hide the column ranges that correspond to the two other dropdown choices (i.e., if you select "HARD" it will hide Columns D-K as well as T-AA).
What am I doing wrong here? I'm sure quite a bit of course.
link
MODIFIED SCRIPT: I got this to work properly for each when run separately, but it requires me to to unhide after each time, otherwise it compounds what is hidden. I have it set to trigger on edit. It's so close, is there something about the trigger or perhaps I need to somehow add something that resets it to unhide all before I can change the selection? (not sure how though)
var ss=SpreadsheetApp.getActive();
var value1 = "NORMAL";
var value2 = "HARD";
var value3 = "MAX POINTS";
var activeSheet = ss.getActiveSheet();
var cell = activeSheet.getRange("B2").getValue();
function HideColumn() {
if(cell == value1) {
activeSheet.hideColumns(12, (27-7+1));
}
else if(cell == value2) {
activeSheet.hideColumns(4, (14-7+1));
activeSheet.hideColumns(21, (14-7+1));
}
else if(cell == value3) {
activeSheet.hideColumns(4, (22-7+1));
}
}
You have a dropdown in Cell B2.
There are three options: "Normal", "Hard", and "Max Points".
Depending on the value selected, you want to show the columns for the selected value and hide the columns for the other options.
The script uses the switch statement Doc ref as an alternative to an IF statement.
To run this answer:
copy to the Project editor,
create an installable onEdit() trigger. (enables the script to run when the dropdown cell is edited; and enables Event objects to be used)
// running as an installable onEdit() trigger
// watching cell B2
function showHideColumns(e){
var sheetName = "Today's Matchups"
// Logger.log(JSON.stringify(e)) // DEBUG
if (e.range.columnStart ==2 && e.range.rowStart ==2 && e.range.getSheet().getName() == sheetName){
// correct sheet and correct cell
// Logger.log("DEBUG: correct sheet and correct cell")
}
else{
// not the correct sheet/cell
// Logger.log("DEBUG: not the correct sheet/cell")
return
}
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName(sheetName)
var value1 = "NORMAL";
var value2 = "HARD";
var value3 = "MAX POINTS";
var value1ColStart = 4
var value2ColStart = 12
var value3ColStart = 20
var valueSetNumCols = 8
// Columns D-K ("NORMAL"),
// Columns L-S ("HARD"),
// Columns T-AA ("MAX POINTS")
var cell = e.value
// Logger.log("DEBUG: dropdown value = "+cell)
switch (cell) {
case value1:
// NORMAL
// show NORMAL, hide Hard & Max Points
// show all columns (including NORMAL)
sheet.showColumns(value1ColStart,24)
// hide Hard
sheet.hideColumns(value2ColStart, valueSetNumCols)
// hide MaxPoints
sheet. hideColumns(value3ColStart, valueSetNumCols)
break
case value2:
// HARD
// show HARD, hide Normal & Max Points
// show all Columns
sheet.showColumns(value1ColStart,24)
// hide Normal
sheet.hideColumns(value1ColStart, valueSetNumCols)
// hide MaxPoints
sheet. hideColumns(value3ColStart, valueSetNumCols)
break
default:
// Max Points
// show Max Points, hide Normal & Hard
// show all Columns
sheet.showColumns(value1ColStart,24)
// hide Normal
sheet.hideColumns(value1ColStart, valueSetNumCols)
// hide Hard
sheet. hideColumns(value2ColStart, valueSetNumCols)
}
}

How to show specific columns based on cell value in google sheets

I'm trying to write script that will achieve the following logic:
If cell J1 has a value of 1, show columns 1-10. If cell J1 has a value of 2, show columns 1-18. If cell J1 has a value of 3, show columns 1-26. If cell J1 has a value of 4, show columns 1-36.
Here' what I have so far:
function onOpen(e) {
hideVersions_(e);
}
function hideVersions_() {
// get current sheet
var sheet = e.source.getActiveSheet();
// get row and column of edited cell
var row = e.range.getRow();
var col = e.range.getColumn();
if (col == 10 && row == 1 && sheet.getSheetName() == 'Design') {
if (e.value === "2") {
sheet.hideColumns(19, 17); // hide column 19-36 (S-AJ)
sheet.showColumns(1,17); // show column 1-18 (A-R)
}
if (e.value === "3") {
sheet.hideColumns(27, 9); // hide column 27-36 (AB-AJ)
sheet.showColumns(1,25); // show column 1-26 (A-AA)
}
if (e.value === "4")
sheet.showColumns(1,35); // show column 1-36 (A-AJ)
}
else {
sheet.hideColumns(11, 25); // hide column 11-36 (K-AJ)
sheet.showColumns(1,9); // show column 1-10 (A-J)
}
}
As I tried to get this to work I noticed you had an Underscore on your Function Name
function hideVersions_() {
That kept me from running and testing that Function.
You also Passed e from the onOpen Event into the hideVersion Function, but did not declare e as a parameter.
Instead of the onOpen Event do you want to use the onChange Event? Even though you might be making other changes we can modify the script to only look at cell J1 on a specific sheet. You'll notice in the code below I added an if statement to check if e is Null, this allowed me to test the file in the editor with out the triggering event.
I think your logic in your if statement can be simplified, not sure why you are checking if the sheet is a certain sheet of the spreadsheet. I also tend to grab the Value I want instead of letting the e event be the most recently edited cell. This is why I removed the row and col check from your first if statement.
In your if statements you "stringed" your values, but I am assuming you are putting numbers into the cell, and therefore your === is expecting numbers on both sides. Removing the quotes fixed that issue.
In your if statements you hide the new cells and then show cells, I think this can be simplified by Showing all cells and then hiding the new cells.
Below is the code that I got to work that completes what you asked for:
function onOpen(e)
{
hideVersions(e);
}
function hideVersions(e)
{
// get current sheet
if ( e == null) // When you run this Function in Editor
{
var sheet = SpreadsheetApp.getActiveSheet();
}
else
{
var sheet = e.source.getActiveSheet();
}
// get Control Cell J1
var controlValue = sheet.getRange(1,10).getValue(); // J1 is Row 1 Col 10
Logger.log(controlValue);
var lastColumn = sheet.getLastColumn();
if (sheet.getSheetName() == 'Design')
{ //since we grabed the cell explicitly, only Need to check SheetName
if (controlValue === 2)
{
Logger.log("Hiding s-AJ");
sheet.showColumns(1, lastColumn); // show All
sheet.hideColumns(19, 17); // hide column 19-36 (S-AJ)
}
if (controlValue === 3)
{
Logger.log("Hiding AB-AJ");
sheet.showColumns(1, lastColumn) //Show All
sheet.hideColumns(27, 9); // hide column 27-36 (AB-AJ)
}
if (controlValue === 4)
{
Logger.log("Showing all");
sheet.showColumns(1,lastColumn); // show all
}
else
{
Logger.log("Hiding K-AJ");
sheet.showColumns(1, lastColumn); // Show All
sheet.hideColumns(11, 25); // hide column 11-36 (K-AJ)
}
}
}
This is what the onOpen() event object looks like:
{"authMode":"LIMITED","range":{"columnEnd":1,"columnStart":1,"rowEnd":1,"rowStart":1},"user":{"email":"redacted","nickname":"redacted"},"source":{}}
Please note: no e.value

onSelectionChange for a specific Google Sheets tab

Here is my test sheet.
Goal: whenever I click on cells A5:A10 in 'Sheet 1', I want the value of A1 to change to B5:B10.
For example: if I click A7, A1 = B7.
Note: I don't want this script to run for any other sheet or document.
Can you please help me create a script to run automatically for this purpose?
Explanation:
Indeed, the onSelectionChange(e) trigger is what you are looking for.
You just need to take advantage of the event object to capture information of the selected cell.
When you click on a particular cell in range A5:A10 of Sheet1 the following script will update the value of cell A1 to the corresponding value of B5:B10.
What is important here is to understand the if condition I used:
if (as.getName() == 'Sheet1' && row>4 && row<11 && col==1)
Essentially, I am asking for selections only in Sheet1, after row 4 and before row 11 and column 1. That is basically the range A5:A10.
Solution:
function onSelectionChange(e) {
const as = e.source.getActiveSheet();
const row = e.range.getRow();
const col = e.range.getColumn();
if (as.getName() == 'Sheet1' && row>4 && row<11 && col==1){
as.getRange('A1').setValue(as.getRange(row,2).getValue());
}
}
You could also use offset to get the value of the next column instead of hardcopying the number 2.
Replace:
as.getRange('A1').setValue(as.getRange(row,2).getValue());
with:
as.getRange('A1').setValue(e.range.offset(0,1).getValue());
but both approaches work just as fine.
As an alternative to what Marios suggests, I prefer exiting as early as possible (since the onSelectionChange can fire very rapidly, I find it somewhat more performant). So, you can move your check to the top of the function (the rest still apply):
function onSelectionChange({ range }) {
const sh = range.getSheet();
const shname = sh.getSheetName();
if( shname !== "<sheet name here>" ) { return; }
//continue if ok
}
Note that usually, it is better to put the sheet name in a configuration object (or, even better, in a function that returns a configuration object) for easy maintenance.
Also, since each sheet has a unique Id (you can visually find it in the gid anchor of the open spreadsheet URL or programmatically with the method mentioned below), you could save you some trouble if the sheet gets renamed and check for id match instead with getSheetId:
function onSelectionChange({ range }) {
const sh = range.getSheet();
const id = sh.getSheetId();
if( id !== 123456789 ) { return; }
//continue if ok
}

Only one TRUE checkbox

I have a column of check boxes:
.
If a box is checked it sets a value to a cell in another sheet.
If I check box no.1 ,it turns true and the remaining still false
then if I check box no.2 it also turns true long with box no.1 and the remaining still false. This is the normal operation but I need that, when I check a box it turns true and all the other boxes turn false, either they are checked or not.In other words, I want one box to be checked at a time.
Can I do that?
This is my code to set a value if the box is checked:
var hasValue = sheet.getRange("B2:B").getValues();
for (var i = 0; i < hasValue.length; i++) {
if (hasValue[i][0] == true) {
var transfer = sheet2.getRange(2, 2, 1, 1).setValue(i + 1);
}
}
This kind of behavior is known as a "radio button".
The simplest method to achieve it is to bind the simple edit trigger:
inspect the edited range to determine if it was to your checkbox region and quit if not.
set all checkboxes to false
set the edited cell to the appropriate value from the event object
if required, perform the update
An extremely minimal sample which you will have to configure, and which is only configured for single-cell edits.
function onEdit(e) {
if (!e || e.value === undefined)
return; // The function was run from the Script Editor, or a multi-cell range was edited.
const edited = e.range;
const s = edited.getSheet();
if (s.getName() !== "some name")
return; // A cell on the wrong sheet was edited
if (isCheckboxRow_(edited.getRow()) && isCheckboxCol_(edited.getColumn())) {
// The cell edited was in a row and a column that contains a checkbox
updateCheckboxes_(s, edited, e);
}
}
function isCheckboxRow_(row) {
// Assumes checkboxes are only in rows 5, 6, 7, 8, 9, and 10
return row >= 5 && row <= 10;
}
function isCheckboxCol_(col) {
// Assumes checkboxes are in column A
return col === 1;
}
function updateCheckboxes_(sheet, editRange, eventObject) {
if (!sheet || !edit || !eventObject)
return; // Make sure all required arguments are defined (i.e. this was called and not run from the Script Editor)
const cbRange = sheet.getRange("A5:A10"); // location of the checkboxes in a radio group.
cbRange.setValue(false);
editRange.setValue(eventObject.value);
// Reference some other sheet
const targetSheet = eventObject.source.getSheetByName("some other sheet name")
if (!targetSheet)
return; // the sheet name didn't exist in the workbook we edited.
// Reference a cell in the same row as the cell we edited, in column 1
const targetCell = targetSheet.getRange(editRange.getRow(), 1);
if (eventObject.value) {
// when true, give the target cell the value of the cell next to the edited checkbox
targetCell.setValue(editRange.offset(0, 1).getValue());
// do other stuff that should be done when a checkbox is made true
} else {
// the checkbox was toggled to false, so clear the target cell
targetCell.clear();
// do other stuff that should be done when a checkbox is made false
}
}
The above hints at some suggested practices, such as using helper functions to encapsulate and abstract logic, resulting in easier to understand functions.
Review:
Simple Triggers
Event Objects
Spreadsheet Service
As I mentioned I would us an onEdit(event) to monitor which checkbox has been checked and loop through the column and only set one checkbox to true. Note that in your code snippet, getRange("B2:B") could be 999 rows. I use getDataRange() to limit to only the rows that are used. And I use getCriteriaType() to check that it is a checkbox not some other data type. And I'm assuming on your sheet2 you want to record which box was last checked true. tehhowch's answer is more generic and maybe more than what you need so here is a limited specific answer.
function onEdit(event) {
try {
var sheet = event.range.getSheet();
// Limit the following code to a particular sheet
if( sheet.getName() === "Sheet5" ) {
// Limit the following code to column B
if( event.range.getColumn() === 2 ) {
var range = sheet.getRange(2,2,sheet.getLastRow()-1,1);
var checks = range.getValues();
var valid = range.getDataValidations();
for( var i=0; i<checks.length; i++ ) {
if( valid[i][0].getCriteriaType() === SpreadsheetApp.DataValidationCriteria.CHECKBOX ) checks[i][0] = false;
}
// Assuming there are no formulas in this range
range.setValues(checks);
event.range.setValue(event.value);
if( event.value === true ) {
event.source.getSheetByName("Sheet6").getRange(2,2,1,1).setValue(event.range.getRow());
}
}
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}

google script conditional formatting

I have put together a google spreadsheet that highlights the first four cells in a row if the value in cell 6 is within 3 days of the current date, but I cannot figure out how to add an if/and statement to exclude all entries that have "East" in row E.
This is the code that I have had success with, minus the exclusion of East not highlighting.
onEdit(e) {
if (e) {
var ss = e.source.getActiveSheet();
var r = e.source.getActiveRange();
if (r.getRow() != 2 && ss.getName() == "Sheet1") {
Days_Remaining = ss.getRange(r.getRow(),6).getValue();
rowRange = ss.getRange(r.getRow(),1,1,4);
if (Days_Remaining < 3.8) {
rowRange.setBackgroundColor("#FF0000");
} else if (Days_Remaining == 'N/A') {
rowRange.setBackgroundColor("#ffffff");
} else if (Days_Remaining > 3.9) {
rowRange.setBackgroundColor("#ffffff");
https://docs.google.com/spreadsheet/ccc?key=0AqtCuK7ex8ZNdGxKLUZpQnZ3UzRCV3VoclVDbFVqQnc#gid=0
If you use the new Google Sheets, you can simplify by using Conditional Formatting
If not, you need to get the values in column 5 as well and add an IF block based on that. For example:
Follow_up = ss.getRange(r.getRow(),5).getValue();
And then, before you check Days_Remaining, you should add
If(Follow_up <> "East") {//rest of the checks here }