I have a Google spreadsheet which has checkboxes in column P, as and when a checkbox is ticked the corresponding row automatically hides.
I am looking to add an additional check box in G1 that will show all hidden rows if checked and if possible a checkbox in E1 to then hide again all the rows that have the checkbox ticked in column P.
Basically I want to automatically hide rows when I tick the checkbox in column P
Then later I may need to review the hidden rows by clicking the checkbox in G1.
When I am finished reviewing the hidden rows above click a checkbox in E1 and all rows which are checked in column P will hide again.
function onEdit(e){
if (e.range.columnStart != 16 || e.value != "TRUE") return;
SpreadsheetApp.getActiveSheet().hideRows(e.range.rowStart);
}
Solution:
Check if edited cell is G1 via getA1Notation(). In that case, check if value is TRUE, and if that's the case, show all rows. An easy option to show all rows is using unhideRow(row), setting A:A as your range (since this includes all rows in the sheet).
Check if edited cell is E1. If the value is TRUE, retrieve all values from P column via getValues(), iterate through them, and hide the corresponding rows if the value is true, using hideRows(rowIndex).
Check if the edited cell is in column P, and use hideRows on the edited row if that's the case, as you're already doing.
Code snippet:
function onEdit(e){
const sheet = SpreadsheetApp.getActiveSheet();
const range = e.range;
const editedValue = e.value;
if (range.getA1Notation() === "G1" && editedValue === "TRUE") {
sheet.unhideRow(sheet.getRange("A:A"));
} else if (range.getA1Notation() === "E1" && editedValue === "TRUE") {
const firstRow = 2;
const lastRow = sheet.getLastRow();
const pValues = sheet.getRange("P" + firstRow + ":P" + lastRow).getValues().flat();
pValues.forEach((pValue,i) => {
if (pValue === true) {
sheet.hideRows(i + firstRow);
}
});
} else if (e.range.columnStart === 16 && editedValue === "TRUE") {
sheet.hideRows(range.rowStart);
}
}
I know it is a bit of an old thread, but still very helpfull to al lot of people i guess. Both the code from the TS and the answer were very helpfull.
I used this for a similar project and found it a bit strange to have two checkboxes to show/hide rows though, so i made this work with one checkbox in cell "G1" to toggle the view hide/unhide.
Code Snippet
function onEdit(e){
const sheet = SpreadsheetApp.getActiveSheet();
const range = e.range;
const editedValue = e.value;
if (range.getA1Notation() === "G1" && editedValue === "TRUE") {
sheet.unhideRow(sheet.getRange("A:A"));
} else if (range.getA1Notation() === "G1" && editedValue === "FALSE") {
const firstRow = 2;
const lastRow = sheet.getLastRow();
const pValues = sheet.getRange("P" + firstRow + ":P" + lastRow).getValues().flat();
pValues.forEach((pValue,i) => {
if (pValue === true) {
sheet.hideRows(i + firstRow);
}
});
} else if (e.range.columnStart === 16 && editedValue === "TRUE") {
sheet.hideRows(range.rowStart);
}
}
Related
I am trying to get Google Sheets show a popup message whenever a cell changes from FALSE to TRUE. I have found a few ways of doing it using onEdit functions. The problem here is that the Cell in question does not really get edited, but rather changes value due to another set of cells meeting certain criteria. That means onEdit functions won't run when the value changes.
I tried this script, but it does nothing:
//popup message
function alertMessageYesNoButton() {
var result = SpreadsheetApp.getUi().alert("Are you sure you want to send an alert about?", SpreadsheetApp.getUi().ButtonSet.YES_NO);
SpreadsheetApp.getActive().toast(result);
}
function sendMessageYesNo(e){
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE'){
alertMessageYesNoButton();
}else{}
}
If someone could come up with something that works, I would really appreciate it.
From your following reply,
cell D17 changes from FALSE to TRUE when three other checkboxes are checked using this formula: {"";SUMPRODUCT(B16:B18)=COUNTA(B16:B18)}.
In this case, how about checking the checkboxes of "B16", "B17", "B18"? When this is reflected in your showing script, how about the following modification? I thought that when those checkboxes are manually checked, onEdit trigger can be used.
In this modification, when all checkboxes of "B16:B18" of "Sheet1" are checked, your script of alertMessageYesNoButton() is run. So, when you test this, please check all checkboxes of "B16:B18" of "Sheet1". By this, the script is run.
Pattern 1:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
alertMessageYesNoButton(); // This is your script.
}
Pattern 2:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRange = "B16:B18"; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = sheet.getSheetName() == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
if (!condition1 || !condition2 || !condition3) return;
// This is your script.
var cellValue = sheet.getRange(4, 17).getValue();
if (cellValue == 'TRUE') {
alertMessageYesNoButton();
}
}
Reference:
Simple Triggers
Added:
From your following reply,
this works very well for the example I laid out, but I tried using several multiple instances of the same script within the same sheet and it always works only in one of them. So let´s say I have for instances of the same situation (B16:B18), (B13:B15), (D13:D15) and (D16:D18). Only when I check the boxes corresponding to the last function (from top to bottom), the message pops up.
From your question and your reply, I proposed an answer as the checkboxes of "B16:B18". But from your reply, when your checkboxes are "B16:B18", "B13:B15", "D13:D15", "D16:D18", how about the following sample script?
Sample script:
function onEdit(e) {
const sheetName = "Sheet1"; // This is from your script.
const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"]; // This is from your reply.
const { range } = e;
const sheet = range.getSheet();
const actSheetName = sheet.getSheetName();
const res = checkBoxRanges.find(checkBoxRange => {
const r = sheet.getRange(checkBoxRange);
const rowStart = r.getRow();
const rowEnd = rowStart + r.getNumRows() - 1;
const colStart = r.getColumn();
const condition1 = actSheetName == sheetName;
const condition2 = range.rowStart >= rowStart && range.rowEnd <= rowEnd && range.columnStart == colStart;
const condition3 = r.getValues().every(([b]) => b === true);
return condition1 && condition2 && condition3;
});
if (!res) return;
Browser.msgBox(`Checked checkboxes of ${res}`); // Here, you can see the checkbox group that was checked.
alertMessageYesNoButton(); // This is your script.
}
In this case, by giving const checkBoxRanges = ["B16:B18", "B13:B15", "D13:D15", "D16:D18"], the script detects the checked checkboxe group, and show the range as a sample.
Trying to set a formula in a cell only when a checkbox is checked.
So far, the script is as follows, however ends up setting the formula regardless of the checkbox status and doesn't function on the checkbox status.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var cbxRnge = ss.getRange('B3')
if (cbxRnge == true) {
return;
}
var cell = ss.getRange("D3");
cell.setFormula('=IF(F3=1,"YES","NO")');
}
Hoping for the formula to remain in the cell until checkbox is unchecked
Expected Results
Checkbox Unchecked
.
Checkbox Checked
EDIT:
Have thought to remove the formula when the checkbox is false:
if (cbxRnge == false) {
return;
}
var cell = ss.getRange("D3");
cell.clearContent();
however ends up clearing and not returning the function when true.
Sample Sheet
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Sheet1" && e.range.columnStart == 2 && e.range.rowStart == 12 && e.value == "TRUE") {
sh.getRange("D13").setFormula("=J13>=1")
}
}
I am trying to create radio checkboxes, i managed to make it work when the checkboxes are in vertical as shown in the exemple sheet bellow with the tab name 'Radio 1' but i have another tab 'Radio 2' where the checkboxes are aligned horizontally and are not aligned one after the other but after every 2 cells, the first Radio works with the following code
function onEdit(e) {
if (e.range.columnStart != 2 || e.range.rowStart == 1 || e.range.rowStart > 8 || e.value != "TRUE") return;
let r = SpreadsheetApp.getActive().getActiveSheet().getRange(2,2,7);
let checks = r.getValues();
for (let i in checks){
if(checks[i][0] == true && +i != e.range.rowStart - 2)
checks[i][0] = false;
}
r.setValues(checks);
}
but i cant make it to work when adapting to the second radio tab, any ideas?
sheet link
Try
function onEdit(e) {
var sh = e.source.getActiveSheet()
if (sh.getName() == 'Radio 1') {
if (e.range.columnStart != 2 || e.range.rowStart == 1 || e.range.rowStart > 8 || e.value != "TRUE") return;
let r = SpreadsheetApp.getActive().getActiveSheet().getRange(2, 2, 7);
let checks = r.getValues();
for (let i in checks) {
if (checks[i][0] == true && +i != e.range.rowStart - 2)
checks[i][0] = false;
}
r.setValues(checks);
}
else if (sh.getName() == 'Radio 2') {
var cel = e.source.getActiveRange()
if (cel.getRow() == 11 && cel.getValue() == true) {
var r = sh.getRange('H' + cel.getRow() + ':S' + cel.getRow())
var checks = r.getValues()
for (var i=0;i<checks[0].length;i+=2) {
if (checks[0][i] == true && i != cel.getColumn()-8)
if (checks[0][i]==true) {checks[0][i] = false};
}
r.setValues(checks);
}
}
}
If you want to apply to multi-rows, change this cel.getRow() == 11
In your situation, how about the following modification?
Modified script:
In this modification, the cell ranges of checkboxes are used.
function onEdit(e) {
// This is from your sample Spreadsheet.
// The cell ranges of checkboxes are set as A1Notation.
const obj = {
'Radio 1': ["B2", "B3", "B4", "B5", "B6", "B7", "B8"],
'Radio 2': ["H11", "J11", "L11", "N11", "P11", "R11"]
};
const sheet = e.source.getActiveSheet();
const sheetName = sheet.getSheetName();
const a1Notation = e.range.getA1Notation();
if (!obj[sheetName] || !obj[sheetName].includes(a1Notation) || e.range.getValue() === false) return;
sheet.getRangeList(obj[sheetName].filter(e => e != a1Notation)).uncheck();
}
When this script is run, when a checkbox is checked on "Radio 1" or "Radio 2", other checked checkboxes are unchecked.
If you want to add "Radio 3" and others, please add them to obj.
Note:
When you changed the checkboxes and the sheet names, please modify obj. Please be careful about this.
References:
filter()
uncheck() of Class RangeList
I am quite bad at this stuff, I tried searching and have a somewhat workable script (apps script) but I just can't get it to work the way I want it to. I'm sure I added/edited things wrongly.
My Google sheet is a group work tracker and looks like the image below.
The primary need is to add an alert message when someone checks or unchecks any of the checkboxes in those columns (to prevent any misclicks - like so ). We are using custom cell values for checked and unchecked as "1" and "0" respectively. The columns with checkboxes are F,K,P,R,S.
In addition to that, I would like a timestamp (format dd/mm/yyyy) to be populated 2 columns away when any of the checkboxes in columns F&K are checked (and confirms the alert message). However, when an already ticked checkbox is unticked, the date should not be removed or updated.
The below script works to a certain extent, but I don't know how to amend it.
function onEdit(e) {
const sh=e.range.getSheet();
if(sh.getName()=='Video Projects' && e.range.columnStart==6,11,16,18 && e.value=='1') {
var resp=SpreadsheetApp.getUi().alert('Are you sure you want to tick this checkbox?', SpreadsheetApp.getUi().ButtonSet.YES_NO);
if(resp==SpreadsheetApp.getUi().Button.YES) {
var range = e.range;
var sheet = range.getSheet();
if(sheet.getSheetName() == "Video Projects") {
if(range.columnStart == 6) {
var nextCell = range.offset(0, 2);
nextCell.setValue(new Date());
} else if(range.columnStart == 11 && range.isChecked()) {
var nextCell = range.offset(0, 2);
nextCell.setValue(new Date());
}
}
};
}else{
e.range.setValue("0");
}
}
If anyone knows how to fix this, it would be really helpful! :)
I believe your goal is as follows.
You want to check the checkboxes of the columns F,K,P,R,S.
When the checkboxes of columns "F" and "K" are clicked, you want to put the date to the column "H" and "M", respectively.
Modification points:
When e.range.columnStart==6,11,16,18 is used as the if statement, all values of e.range.columnStart are true. I thought that this might be the reason of your issue.
When this point is reflected in your script, it becomes as follows.
Modified script:
function onEdit(e) {
const columns = [6, 11, 16, 18, 19]; // columns F,K,P,R,S
const { range, value } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != 'Video Projects' || !columns.includes(range.columnStart) || value == "0") return;
const ui = SpreadsheetApp.getUi();
const res = ui.alert('Are you sure you want to tick this checkbox?', SpreadsheetApp.getUi().ButtonSet.YES_NO);
if (res == ui.Button.YES) {
if (range.columnStart == 6 || (range.columnStart == 11 && value == "1")) {
range.offset(0, 2).setValue(new Date());
}
return;
}
range.uncheck();
}
References:
Simple Triggers
Event Objects
Added 1:
About your following 2 new requests,
Request 1: could we also add the alert message when unchecking a checkbox too?
Request 2: Is it possible for it to input only the date as dd/mm/yyyy?
The sample script is as follows.
Sample script:
function onEdit(e) {
const columns = [6, 11, 16, 18, 19]; // columns F,K,P,R,S
const { range, value } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != 'Video Projects' || !columns.includes(range.columnStart)) return;
const ui = SpreadsheetApp.getUi();
const res = ui.alert('Are you sure you want to tick this checkbox?', SpreadsheetApp.getUi().ButtonSet.YES_NO);
if (res == ui.Button.YES) {
if (range.columnStart == 6 || (range.columnStart == 11 && value == "1")) {
range.offset(0, 2).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy"));
}
return;
}
range.uncheck();
}
Added 2:
About your following a new request,
Request 1: can be changed to "Are you sure you want to untick this checkbox?"
The sample script is as follows.
Sample script:
function onEdit(e) {
const columns = [6, 11, 16, 18, 19]; // columns F,K,P,R,S
const { range, value } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != 'Video Projects' || !columns.includes(range.columnStart)) return;
const ui = SpreadsheetApp.getUi();
if (value == "0") {
const res = ui.alert("Are you sure you want to untick this checkbox?", SpreadsheetApp.getUi().ButtonSet.YES_NO);
if (res == ui.Button.NO) range.check();
return;
}
const res = ui.alert('Are you sure you want to tick this checkbox?', SpreadsheetApp.getUi().ButtonSet.YES_NO);
if (res == ui.Button.YES) {
if (range.columnStart == 6 || (range.columnStart == 11 && value == "1")) {
range.offset(0, 2).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy"));
}
return;
}
range.uncheck();
}
function onEdit() {
var now = new Date()
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var row = ss.getActiveRange().getRow()
ss.getRange(row, 1).setValue("value for first Col")
ss.getRange(row, 2).setValue("value for second Col")
ss.getRange(row, 3).setValue(now) //third Col with the current date/Time
}
The code above fires every time you edit any cell in the doc.
To prevent this you can add a if/else statement to check the Column like so.
var col = ss.getActiveRange().getColumn()
if (col == 2){//replace the 2 with the checkbox Column.
//do this
}
Now the Code fires every time you edit a cell in the second col and adds values to the same row.
trying to make a semi/manual tree structure. if check box = true. in a topdown manner. it will look for another check box in the downside direction and unhide rows. i.e. show A2 to A9. if box = false then, Hide: A11 till A19. if there is no check box until the last row it will hide/show all the rows. i.e. B23/B2000. it's a top-down approach so the only action only is limited to a particular column and between the boxes cells are blanks.
function onEdit(e){
const sheet = SpreadsheetApp.getActiveSheet();
const range = e.range;
const editedValue = e.value;
if (range.getA1Notation() === "A1" && editedValue === "TRUE") {
sheet.unhideRow(sheet.getRange("A:A9"));
} else if (range.getA1Notation() === "A1" && editedValue === "TRUE") {
const firstRow = 1;
const lastRow = sheet.getLastRow();
if (pValue === true) {
sheet.hideRows(i + firstRow);
}
});
} else if (e.range.columnStart === 9 && editedValue === "TRUE") {
sheet.hideRows(range.rowStart);
}
}
Solution:
You can use this script to check the rows below the edited checkbox and hide/show rows accordingly:
function onEdit(e){
const sheet = SpreadsheetApp.getActiveSheet();
const range = e.range;
const editedValue = e.value;
const row = range.getRow();
const col = range.getColumn();
const lastRow = sheet.getMaxRows();
var data = sheet.getRange(row+1,col,lastRow-row+1,1).getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][0] === true || data[i][0] === false) {
++i;
break;
}
}
if (i > 1) {
if (editedValue === "FALSE")
sheet.hideRows(row+1,i-1);
else if (editedValue === "TRUE")
sheet.showRows(row+1,i-1);
}
}
Sample Output: