Set row of values based on checkbox in Google Sheets - google-apps-script

I have a sheet where each row has a checkbox in the C column. I want to write my script such that if the checkbox in column C is unchecked, then columns G-K for that row will all be set to “N/A”.
I’ve seen things on here like getRange(“G2:K2”), but the row number is dynamic and I’m not sure how to do that.
I have it in an onEdit function and have the event row and column stored in variables.
Any help is appreciated. Thanks!

Could try a forEach loop. What is does is it reads each line of row col C to determine if its check and set n/a in the columns. So here in this case, it skips the row that is checked.
https://spreadsheet.dev/foreach-loop-in-apps-script
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var data=ss.getDataRange().getValues();
data.forEach(function(row,col){
if (col == '') return;
if (row[2] == true) return; //If colC is TRUE skip
data.getRange(col + 1, 7,1,5).setValue("n/a"); set colG - K as n/a if colC false
});
}

Description
Using the onEdit event object e you can get the range of the edited cell from that using an offset set the values of columns G to K
Script
function onEdit(e) {
if( e.range.getSheet().getName() === "Sheet1" ) { // Make sure we are on the right sheet
if( e.range.getColumn() === 3 ) { // Make sure the edit occured in column C
if( e.value === "FALSE" ) { // Unchecked
e.range.offset(0,4,1,5).setValues([["N/A","N/A","N/A","N/A","N/A"]]);
}
}
}
}
Reference
https://developers.google.com/apps-script/guides/triggers/events
https://developers.google.com/apps-script/reference/spreadsheet/range#offset(Integer,Integer,Integer,Integer)

Related

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

Not only auto fill cells but also delete a row when a checkbox is checked

Using a spreadsheet to track issues for a department at work. Currently I have a script built where when you load the issue identifier (column C), column I auto fills with the current date and time to serve as a timestamp to document when the issue was added.
I also want to build a script where when you check the box in column M, the entire row the checkbox is in deletes itself. In other words, when the issue is resolved (complete) you check the box and the entire row containing the issue deletes itself.
Both scripts need to run on all sheets within the workbook.
Here is the script I have written so far, which works perfectly, to add the timestamp:
function onEdit () {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
if( r.getColumn() == 3 ) {
var nextCell = r.offset(0, 6);
if( nextCell.getValue() === '' )
nextCell.setValue(new Date()).setNumberFormat('MM/dd/yyyy HH:mm:ss');
}
}
SUGGESTION
In my understanding of your question, you are trying to:
Using an onEdit simple trigger, when a cell in column C gets updated, you are placing a timestamp (which already works).
On the same onEdit trigger, you are also trying to delete the row that is adjacent to a checkbox cell that gets checked on column M.
You need to use the deleteRow() method to delete a row. Perhaps you can try using this sample tweaked script below using JavaScript Function call() & Conditional (ternary) operator to have a cleaner & organized script flow.
Sample Script
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
var nextCell = r.offset(0, 6);
const ifColumnC = {
placeTimeStamp: function () {
return nextCell.getValue() === '' ? nextCell.setValue(new Date()).setNumberFormat('MM/dd/yyyy HH:mm:ss') : null;
}
}
const ifColumnM = {
deleteRow: function () {
return r.getValue() == true ? s.deleteRow(r.getRow()) : null;
}
}
/**If Column "C" gets edited call "placeTimeStamp" function
* otherwise if Column "M" checkbox cell gets checked, call the "deleteRow" function
*/
r.getColumn() == 3 ? ifColumnC.placeTimeStamp.call() :
r.getColumn() == 13 ? ifColumnM.deleteRow.call() : null;
}
Demo
NOTE: If there's anything else missing or have been misunderstood, feel free to comment.

How to use on Edit function twice in same spreadsheet [duplicate]

I have script for Google Sheets that I collected on interwebs and got some help here. No I have 2 onEdit in conflict. I overcome that by creating script Trigger for onEdit2. It works but I don't think it is the best solution. Could you help get those two separated onEdit with if functions into one, please?
//Dependent Dropdown list
function onEdit(e){ // Function that runs when we edit a value in the table.
masterSelector(master1,master2,master3,master4);
var activeCell = e.range; // It returns the coordinate of the cell that we just edited.
var val = activeCell.getValue(); // Returns the value entered in the column we just edited.
var r = activeCell.getRow(); // returns the row number of the cell we edit.
var c = activeCell.getColumn(); // returns the column number of the cell we edit.
var wsName = activeCell.getSheet().getName();
if (wsName === masterWsName && c === firstLevelColumn && r > masterNumberOfHeaderRows) { // the if delimits the section sensitive to modification and action of the onEdit.
applyFirstLevelValidation(val,r);
} else if (wsName === masterWsName && c === secondLevelColumn && r > masterNumberOfHeaderRows){
applySecondLevelValidation(val,r);
}
} // end of onEdit
// addRow by checkboxes
function onEdit2(e) {
masterSelector(master1,master2,master3,master4);
//IF the cell that was edited was in column 4 = D and therefore a checkbox AND if the cell edited was checked (not unchecked):
if (e.range.columnStart === 4 && e.range.getValue() === true) {
var sheet = SpreadsheetApp.getActiveSheet(),
row = sheet.getActiveCell()
.getRow(),
//(active row, from column, numRows, numColumns)
rangeToCopy = sheet.getRange(row, 1, 1, 30);
sheet.insertRowAfter(row);
rangeToCopy.copyTo(sheet.getRange(row + 1, 1));
//Reset checked boxes in column 4
sheet.getRange(row,4,2,1).setValue(false);
}
}
Whole script is here, if needed.
A script cannot contain two functions with the same name. Rename your first onEdit function to onEdit1 (actually it will be better to assign a descriptive name) and the second function as onEdit2, then put them both in one function named onEdit and pass the parameter e to both of them:
function onEdit(e){
onEdit1(e);
onEdit2(e);
}
Related:
Two OnEdit functions not working together
Best Practices for Multiple OnEdit Functions
How to run multiple onEdit functions in the same google script (google sheets)?
Bracketing multiple onEdit functions

Google script to insert current date only once in cell when specific values are selected in the same row, different cell

I'm working on a script that is giving me trouble. Column G has picklist values M1, M2,M3,S1,S2,S3,S4,S5,C1,C2,C3. I would like to have a timestamp inserted in column t when any of the S-values (S1,S2,S3,S4,S5) are selected from the picklist. I would like this done on the first occurrence only, not when the picklist value in column g is subsequently updated or changed.
I'm close, but the current script updates the date in the entire column t.
I'd also like to clean up line 9 if possible.
I have this so far:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('ClientList');
var stageValues = s.getRange('g3:g').getValues();
var dateValues = s.getRange('t3:t').getValues();
for (var row in stageValues)
for (var col in stageValues[row])
if (stageValues[row][col] == 'S1' || stageValues[row][col] == 'S2' || stageValues[row][col] == 'S3' || stageValues[row][col] == 'S4' || stageValues[row][col] == 'S5' && dateValues[row][col] == '') {
num = parseInt(row) + 3;
s.getRange('t' + num).setValue(new Date());
}
};
You say picklist values, i'm assuming you mean a dropdown list. But regardless this will work. When ever a cell is manually edited in column G the onEdit trigger will check for an "S" value and the time stamp in column T. If no time stamp is found it will place a new time stamp. "e" is the trigger object containing information about the cell that was edited.
function onEdit(e) {
try {
if( e.range.getSheet().getName() === "ClientList" ) {
if( e.range.getColumn() === 7 ) { // edit in column G
if( e.value.charAt(0) === "S" ) {
if( e.range.offset(0,13,1,1).getValue() === "" ) { // no value in T
e.range.offset(0,13,1,1).setValue(new Date());
}
}
}
}
}
catch(err) {
Logger.log(err);
}
}

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