How to set a cell value as TRUE in Google apps script - google-apps-script

Column A in my sheet has checkboxes. I'm writing a simple script to find the checkbox that is checked (cell value = TRUE), make it unchecked (change it to FALSE), and then check the next checkbox in the column (make that cell value = TRUE).
Here's my code:
function nextCheckbox() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var checkmarks = ss.getRangeByName("update_checkmarks").getValues().flat(); //this range is column A
Logger.log(checkmarks.indexOf(true)); //this logs 8.0, which is the correct row for the checked box in column A
var rowNum = checkmarks.indexOf(true);
Logger.log(rowNum); // this logs 8.0, as expected
var cell = sheet.getRange(rowNum,1);
cell.setValue(false); //nothing happens here...
var cell = sheet.getRange(rowNum + 1,1);
cell.setValue(true); //nothing happens here...
}
Logging logs the expected row number (8.0). But nothing happens when I use setValue. What am I doing incorrectly?

Regarding getting/setting values for checkboxes
Instead of using setValue consider to use check() / uncheck()
The above because checkboxes might use custom values for checked / unchecked states.
Regarding the use of indexOf / getRange
indexOf will return the index using 0 based numbering, this means 0 corresponds to the first value, 1 for the second and so on.
SpreadsheetApp.Sheet.Range.getRange(row,column) requires 1 based indexes, this means that the first row is 1, the second is 2.
Considering the above replace
var rowNum = checkmarks.indexOf(true);
by
var rowNum = checkmarks.indexOf(true) + 1;
Resources
https://developers.google.com/apps-script/reference/spreadsheet/range#check
https://developers.google.com/apps-script/reference/spreadsheet/range#uncheck

Your array starts at zero while your rows start at one. Currently your setting row 7 to be false(which it already is) and row 8 to be true, which it also already is.
Change this line in your code:
var rowNum = checkmarks.indexOf(true);
to be
var rowNum = checkmarks.indexOf(true)+1;
and you should get your expected results.

function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Sheet1' && e.range.rowStart > 1 && e.range.rowStart < 12 && e.range.columnStart == 1 && e.value == "TRUE") {
e.range.setValue("FALSE");//reset
e.source.toast(e.range.getA1Notation());
}
}
Demo:

Related

Google Sheets: Append then Delete rows based on Tickbox condition

I'm attempting to create a spreadsheet to organise products ordered at my workplace.
When an order is received a team member would add the details to the sheet; when it is collected they'd fill out date and ID then tick the order complete. See Attached
What I want to happen next is that the row containing the complete details from that order is appended to a second page in the sheet and the original row is deleted.
I can't make sense of how to get this to run automatically when the box is checked; so far I have been compiling a script to run from a button press:
function runFiling() {
function moveRows() {
var ss = SpreadsheetApp.getActive();
var osh = ss.getSheetByName('Current');
var dsh = ss.getSheetByName('Collected');
var srg = osh.getDataRange('H2:H');//You might want to specify a more unique range. This just gets all of the data on the sheet
var svA = srg.getValues();
var d=0;//deleted row counter
for(var i=1;i<svA.length;i++) {
if(svA[i][7] =='TRUE') {
dsh.appendRow(svA[i]);//append entire row to Sheet2
osh.deleteRow(i-d+1);//accounts for the difference between length of array and number of remaining row.
d++;
}
}
}
}
However even this fails to Append or Delete anything although no errors are found/returned.
If anyone can suggest a way to fix the above or, preferably, how to make the script work when the box is ticked your help will be most appreciated.
Try it this way using an onEdit(e) function
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == 'Current' && e.range.columnStart == 7 && e.range.rowStart > 1 && e.value == "TRUE") {
const dsh = ss.getSheetByName('Collected');
const vs = sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).getValues()
dsh.getRange(dsh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);
sh.deleteRow(e.range.rowStart);
}
}
This will accomplish the task line by line as the check boxes are checked off by the user.

Transferring Rows between Sheets via a like Identifier

Evening everyone!
I asked this about a week back, but I think the thread got lost in the ether. We came close, but I'm trying to create a function where "Transfer a range of Rows from sheet 1 to sheet 2. Sheet 1 has order IDs in column E. G will have =unique to show me all current order IDs, with check boxes next to each unique reference. Check the box next to which ones you want to CUT over > Select a menu run add on > Run Script > all Rows from A:E that match the desired ID are moved".
[Picture Reference]
Sheet Reference
function onEdit(e) {
e.source.toast('Entry')
const sh = e.range.getSheet();
if(sh.getName() == "Reference" && e.range.columnStart == 8 && e.range.rowStart > 1 && e.value == "TRUE") {
e.source.toast('Gate1')
let rg = sh.getRange(e.range.rowStart,1,1,5)
let vs = rg.getValues();
const osh = e.source.getSheetByName("Processing");
osh.getRange(osh.getLastRow() + 1,1,1,5).setValues(vs);
rg.deleteCells(SpreadsheetApp.Dimension.ROWS);
e.range.setValue("FALSE");
}
}
Here is what we had so far. Please let me know if anyone can help, thank you!
To get all rows that match the unique ID whose checkbox was ticked, use Array.filter(), like this:
/**
* Simple trigger that runs each time the user hand edits the spreadsheet.
*
* #param {Object} e The onEdit() event object.
*/
function onEdit(e) {
if (!e) {
throw new Error(
'Please do not run the onEdit(e) function in the script editor window. '
+ 'It runs automatically when you hand edit the spreadsheet.'
+ 'See https://stackoverflow.com/a/63851123/13045193.'
);
}
moveRowsByUniqueId_(e);
}
/**
* Triggers on a checkbox click and moves rows that match a unique ID.
*
* #param {Object} e The onEdit() event object.
*/
function moveRowsByUniqueId_(e) {
let sheet;
if (e.value !== 'TRUE'
|| e.range.rowStart <= 1
|| e.range.columnStart !== 8
|| (sheet = e.range.getSheet()).getName() !== 'Reference') {
return;
}
e.source.toast('Moving rows...');
const uniqueId = e.range.offset(0, -1).getValue();
const range = sheet.getRange('A2:E');
const values = range.getValues();
const targetSheet = e.source.getSheetByName('Processing');
const _matchWithId = (row) => row[4] === uniqueId;
const valuesToAppend = values.filter(_matchWithId);
if (uniqueId && valuesToAppend.length) {
appendRows_(targetSheet, valuesToAppend);
range.clearContent();
const remainingValues = values.filter((row) => !_matchWithId(row));
range.offset(0, 0, remainingValues.length, remainingValues[0].length)
.setValues(remainingValues);
e.source.toast(`Done. Moved ${valuesToAppend.length} rows.`);
} else {
e.source.toast('Done. Found no rows to move.');
}
e.range.setValue(false);
}
For that to work, you will need to paste the appendRows_() and getLastRow_() utility functions in your script project.
It work almost like asked but :
it's using a personal lib (available below)
didn't make the part realtiv of removing range and aggregate result, I hope i can add it to the lib some day. However, empty cell are fill with -
for an obscure reason, it doesn't like the TRUE/FALSE cell, but work like a charm with 1/0 or any other texte value, regex, ...
Additional error handling are to be added if not any match or others possibilites
function onEdit(e){
console.log(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("H3").getValue())
var tableReference = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("A1").getDataRegion());
var tableReferenceId = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference").getRange("G11").getDataRegion());
var tableProcessing = new TableWithHeaderHelper(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Processing").getRange("A1").getDataRegion());
// get value
var id = tableReferenceId.getTableWhereColumn("Move to Sheet").matchValue(1).getWithinColumn("Unique Filter").cellAtRow(0).getValue();
var tableWithinId = tableReference.getTableWhereColumn("Shipment ID").matchValue(id)
for(var i=0 ; i < tableWithinId.length() ; i++){
var rangeRowWithinId = tableWithinId.getRow(i);
tableProcessing.addNewEntry(rangeRowWithinId);
for(var cell in rangeRowWithinId.getRange()) cell.setValue("-");
}
//reset value
tableReferenceId.getTableWhereColumn("Move to Sheet").matchValue(1).getWithinColumn("Move to Sheet").cellAtRow(0).setValue(0)
}
See below the app script file you need to create in order to use this utils function:
https://github.com/SolannP/UtilsAppSsript/blob/main/UtilsGSheetTableHelper.gs

Hide columns based on checkbox value

I would like to implement a code to my production plan. Is there a way to hide column based on the checkbox true value? Lets say I added the checkboxes to the row 5, and I want to hide each column starting from M column separately upon ticking the checkbox. I implemented the code to hide the rows, but for columns it's getting tricky since I'm not experienced in google script.
Would it be perhaps also possible to add custom filter to show all hidden columns at once, lets say if I wanna make changes in one of them, and then hide all of them at once?
Hide columns starting with M
function onEdit(e) {
e.source.toast("Entry")
const sh = e.range.getSheet();
if (sh.getName() == "Enter Your Sheet Name" && e.range.columnStart > 12 && e.range.rowStart == 5 && e.value == "TRUE") {
e.source.toast("Flag1");
sh.hideColumns(e.range.columnStart);
e.range.setValue("FALSE");
}
}
Yes, these is! If you use an onEdit function, you can use e.range.isChecked() to see if a checkbox was checked. After that, you'd just need to run activeSheet.hideColumns(editedColumn, 1) to hide the column.
This code should do the trick:
function onEdit(e) {
var activeSheet = SpreadsheetApp.getActiveSheet();
var editedRange = e.range;
var editedRow = editedRange.getRow();
var editedColumn = editedRange.getColumn();
//If the checkbox isn't checked, return
if (!editedRange.isChecked()) return;
//If the edited row isn't in row 5, return
if (editedRow != 5) return;
//If the edited column is before M, return
if (editedColumn < 12) return;
//Hide the edited column
activeSheet.hideColumns(editedColumn, 1);
}

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
}

Checkbox ticked/not ticked to prompt action in different cells in Google Sheet

Does anyone recommend a script in Google Sheets, so when a checkbox is ticked the current date is printed in a different cell, please? It's important to note that some scripts work with blank/ticked checkbox. But, my need is to print the date 'ONLY' when a checkbox is ticked, please.
Your IF statement doesn't test for whether the checkbox is ticked or not ticked.
It's possible to set values for the checkbox, but the default values are "TRUE" (ticked) and "FALSE" (unticked). You can establish this simply by entering +A1 in a cell to display the value of a checkbox in Cell A1.
The following code varies from your code in two respects.
var val = e.value;
This assigns the value of the edited cell to a variable.
if(col == 1 && val == "TRUE") {
This expands the IF statement to test if the edited cell was in column A AND whether the value of the edited cell is TRUE (the checkbox was ticked).
function onEdit(e) {
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
var val = e.value;
if(col == 1 && val == "TRUE") {
var adjacentCell = aCell.offset(0,1);
var newDate = Utilities.formatDate(new Date(), "GMT+1", "dd/MM/yyyy");
adjacentCell.setValue(newDate);
}
}
On a tangent, certain Event Objects are returned by the 'e' attribute. You can display these simply by inserting Logger.log(JSON.stringify(e)); into the code. It's possible (and arguably preferable) to use the Event Objects to obtain variables in almost all cases.
For example, in the context of your code:
var erange = e.range; returns the edited range.
var eCol = erange.columnStart; returns the edited column