Only one TRUE checkbox - google-apps-script

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);
}
}

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

I tried a Programmable-Hide-Unhide-Columns-from-Dropdown-Menu

I want that everytime you choose an item from a dropdown menu in A2, it will hide every column and unhide only the pertinents columns for that item.
It's a project where my employees doesn't know Google App script, so I have to try make programmation easy for them and directly on the sheet.
I already tried to make my own code, but it doesn't work. Do you have any idea ?
function onEdit() {
var Sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Feuille 1");
var Menu = Sh.getRange("A2").getValue();
var LastCol = Sh.getDataRange().getLastColumn();
if (Sh.getActiveCell().getColumn() == 1 && (Sh.getActiveCell().getRow() == 1)) {
if (Menu == "Agent 1") {
var FindRow = Sh.getRange("B:B").createTextFinder("Agent 1").findNext().getRow()
var Rg = Sh.getRange(FindRow, 3, 1, LastCol - 2)
var AllemptyArr = Rg.createTextFinder("").findAll();
for (var i = 0; i < AllemptyArr.length; i++) {
Sh.hideColumn(AllemptyArr[i].getColumn());
};
var AllxArr = Rg.createTextFinder("x").findAll();
for (var j = 0; j < AllxArr.length; j++) {
Sh.unhideColumn(AllxArr[j].getColumn());
};
}
}
}
Here is what the sheet look like:
I believe your goal as follows.
You want to show columns of "Column 1" to "Column 6" which has x in the cell value.
"Column 1" to "Column 6" are the column "C" to "H" in the actual range of the sheet.
You want to hide columns of "Column 1" to "Column 6" which has the empty cell.
You want to check the row when the value of the cell "A2" is the same with one of cells "B16:B18".
Modification points:
In your script, this if statement of if (Sh.getActiveCell().getColumn() == 1 && (Sh.getActiveCell().getRow() == 1)) {}, the cell "A1" is checked. By this, when the drop down list of the cell "A2" is changed, the script in the if statement is not run. I think that this is the reason of your issue of nothing happen.
And in your script, from if (Menu == "Agent 1") {}, only when Menu is Agent 1, the script is run. In order to achieve your goal, Agent 1, Agent 2 and Agent 3 are required to be checked.
In this case, I would like to propose the following flow.
Retrieve values from "B16:H18".
Retrieve the row when the value of cell "A2" is the same with the value of cells "B16:B18".
Show and hide rows from the retrieved row.
Modified script:
function onEdit() {
var Sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Feuille 1");
var Menu = Sh.getRange("A2").getValue();
var LastCol = Sh.getDataRange().getLastColumn();
if(Sh.getActiveCell().getColumn() == 1 && (Sh.getActiveCell().getRow() == 2)) {
// 1. Retrieve values from "B16:H18".
var values = Sh.getRange("B16:H18").getValues();
// 2. Retrieve the row when the value of cell "A2" is the same with the value of cells "B16:B18".
var selected = values.filter(([b]) => b == Menu);
// 3. Show and hide rows from the retrieved row.
if (selected.length > 0) {
var columnOffset = 2;
selected[0].splice(0, 1);
selected[0].forEach((e, i) => Sh[e == "x" ? "showColumns" : "hideColumns"](i + 1 + columnOffset));
}
}
}
Note:
I think that in your case, the event object of OnEdit event trigger can be used. But I thought that you might want to run the script using the script editor, custom menu and a button. So in this modification, I didn't use the event object.
References:
showColumns(columnIndex)
hideColumns(columnIndex)

Apply conditional formatting

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.

Google Spreadsheet conditional on three cells

I've been trying to implement a conditional on my spreadsheet, basically a check-sheet with three conditional cells with "Yes" or "No" in them. All I want to achieve (using onEdit) is one all three cells contain "Yes", enter the next column with the date the final Yes was entered. I've managed to create other scripts which work fine, but this one has me stumped.
Thanks
Since the cells can be edited individually, your onEdit will always need to check all of your conditional cells' values, and write the timestamp only when all are "Yes".
function onEdit(event) {
var conditionalCells = [ "B1", "B2", "B3" ]; // Array of monitored conditionals
var inList = false; // assume edit was outside of the conditionals
var allYes = true; // and that all values are "Yes".
var sheet = event.source; // Sheet that was edited
var cell = event.range.getA1Notation(); // get range description
// Loop through all conditionals checking their contents.
// Verify that the edit that triggered onEdit() was in one
// of our conditional cells, setting inList true if it was.
for (var i = 0; i < conditionalCells.length && allYes; i++) {
if (cell == conditionalCells[i]) inList = true;
allYes = (sheet.getRange(conditionalCells[i]).getValue() == "Yes");
};
// If this was our final Yes, record the date.
// By validating inList, we ensure we record only the first time
// all conditionals are "Yes".
if (inList && allYes) sheet.getRange("C1").setValue(new Date());
}