I need to unhide a group of rows when a checkbox is selected (hide on uncheck). I have multiple checkboxes on a singe sheet. I want the user to be able to reveal only the sections they needs to use, all others remain hidden. I seem only to be able to make one work using " if (e.range.columnStart != 1 ) return;" by row. I'm not finding the examples I need to make this work by the specific cell, then repeat for next group. Can someone help?
function onEdit(e){
if ( e.range(1,22).cellStart != 1 ) return;
SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
if ( e.value != "FALSE"){
sheet.showRows(23,20); // Shows rows
}else{
sheet.hideRows(23,20); // Hides rows
}
}
function onEdit(e){
if ( e.range(1,45).cellStart != 1 ) return;
SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
if ( e.value != "FALSE"){
sheet.showRows(46,64); // Shows rows
}else{
sheet.hideRows(46,64); // Hides rows
}
}
Related
Edited for clarity and to add images
Using Google Apps Script, how can I:
copy a range from Section 2 sheet (G11:H11, with a checkbox & dropdown) into range G12:G25 N number of times (based on the number of non-empty rows in MASTER DROPDOWN sheet under same header title as 'Section 2'!A2) and then,
set a different value in each dropdown (each unique value listed in MASTER DROPDOWN sheet under the correct header).
For example, first image is "MASTER DROPDOWN" sheet.
This second image is "Section 2" sheet. The user can add or delete items on the list using the buttons on the right side of the page.
And this last image is "Section 2" sheet. I cannot understand how to write the code for this... When user presses "Reset list" button, I want to copy checkbox and dropdown menu (from G11:H11) N number of times (N=3 based on number of items from MASTER DROPDOWN under Section 2). In each dropdown, I want to set value with each item from the original list in the MASTER DROPDOWN sheet. This process should be dynamic and work on Section 1 and Section 3 sheet (not in worksheet currently).
Any advice on the script verbage to search/learn about this type of functionality, or some direction on the script for this is much appreciated. Here's a link to my code that I have so far...
https://docs.google.com/spreadsheets/d/1ZdlJdhA0ZJOIwLA9dw5-y5v1FyLfRSywjmQ543EwMFQ/edit?usp=sharing
function newListAlert (){
var ui = SpreadsheetApp.getUi();
var response = ui.alert("Are you sure you want to delete your current list and create a new one?",ui.ButtonSet.YES_NO);
if(response == ui.Button.YES) {
newList();
} else {
}
}
function newList() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = ss.getRange("G11:H25");
var options = {contentsOnly: true, validationsOnly: true};
//clear current list
range.clear(options);
//add new item to list in first row of range
addNewItem();
//copy new datavalidation row above based on number of non-empty rows in MASTER DROPDOWN with same header as active sheet (-1)
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MASTER DROPDOWN");
var range = ss.getRange("A2");
if (range.getCell(1,1)){
var section = datass.getRange(1,1,1,datass.getLastColumn()).getValues();
var sectionIndex = section[0].indexOf(range.getValue()) + 1;
var validationRange = datass.getRange(4,sectionIndex,19);//19 columns: checklist has a maximum of 18 rows (+ 1 for "select option")
}
}
In your situation, how about modifying newList() as follows?
Modified script:
function newList() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var sheetName = sheet.getSheetName();
sheet.getRange("G11:H25").clear({ contentsOnly: true, validationsOnly: true });
var srcSheet = ss.getSheetByName("MASTER DROPDOWN");
var values = srcSheet.getDataRange().getValues();
var obj = values[0].map((_, c) => values.map(r => r[c])).reduce((o, [h, , , ...v], i) => {
if (h != "") {
v = v.filter(String);
v.shift();
o[h] = { values: v, range: srcSheet.getRange(4, i + 1, v.length + 1) };
}
return o;
}, {});
if (obj[sheetName]) {
var validationRule = SpreadsheetApp.newDataValidation()
.setAllowInvalid(false)
.setHelpText('Select an option from the menu. To add more options to the dropdown list, go to MASTER DROPDOWN tab.')
.requireValueInRange(obj[sheetName].range, true)
.build();
var d = obj[sheetName].values.map(_ => [validationRule]);
var v = obj[sheetName].values.map(e => [e]);
sheet.getRange(sheet.getLastRow() + 1, 8, obj[sheetName].values.length).setDataValidations(d).setValues(v).offset(0, -1).insertCheckboxes();
}
}
When this script is run, the values for DataValidations are retrieved from the sheet "MASTER DROPDOWN", and using the sheet name, the dataValidation rules are created, and put to the column "H". And also, the checkboxes are put to the column "G" of the same rows of the dataValidations.
In this case, for example, when you add a new sheet of "Section 1" and run newList(), the dropdown list including "Engineering" and "Design" is put to the column "H" and the checkboxes are also put to the column "G".
Note:
In this modification, the sheet name like "Section 2" is used for searching the column of "MASTER DROPDOWN" sheet. So please be careful about this.
And, from your current script, the last row is used for putting to the dropdown list and checkboxes. So when you want to modify this, please modify the above script.
This sample script is for your sample Spreadsheet. So when your actual Spreadsheet is changed, this script might not be able to be used. Please be careful this.
References:
setDataValidations(rules)
insertCheckboxes()
I have this code that will move an entire row of data from one sheet to another within the same workbook based on Checkbox value:
function onEdit(e){
// WORKING
const src = e.source.getActiveSheet();
const r = e.range;
if (r.rowStart == 1 || r.columnStart != 3) return;
let dest;
if (src.getName() == "New")
dest = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Complete");
else if (src.getName() == "Complete")
dest = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("New");
src.getRange(r.rowStart,1,1,3).moveTo(dest.getRange(dest.getLastRow()+1,1,1,3));
src.deleteRow(r.rowStart);
// NOT WORKING
src.getRange("A2:Z").sort([{column: 2, ascending: true}]);
dest.getRange("A2:Z").sort([{column: 2, ascending: true}]);
}
In addition, after the moving has taken place, I am trying to get both the sheets to "re-sort". I have tried with a filer on the current column, but I than have to manually re-filter after the moving. "Not working" = no sorting is taking place after the rows are moved!
How can both sheets be re-sorted after the rows has been triggered to move?
Add (assuming that your script gives the result you expect):
SpreadsheetApp.flush();
before sorting
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#flush
The Google Sheet I have uses code made by user Max Makhrov, code here, to make multiple dependent dynamic dropdowns in columns D-F (for location) and columns H-L (for objectives & activities) in my sample sheet here.
I would like help to modify the script to do two things:
Whatever activity is selected from the dropdown menu in Column I, I would like the same dropdown menu options to be available (to repeat) for columns J-L. As you can see I found a way to do it, but to me it seems clunky and not ideal, and leaves too much room for errors. Users should not select the activity twice, but I've put conditional formatting in to flag that if they do. However:
Ideally, but less importantly, if the dropdown menu items could still repeat for columns J-L but once an activity is selected in previous cells, that option is removed from each of the following repeated dropdown menus in additional columns, up to and including column L. This would help avoid accidentally repeating an activity.
NB: Reference question "How do you do dynamic / dependent drop downs in Google Sheets?"
Thank You!
When one of the drop-down cells is edited you can use an onEdit trigger [1] to iterate through the 4 columns (I-L) and update the drop-downs in each cell removing the option selected in the edited cell. You also need to add the old selected value (previously deleted from other options) to the other drop-downs. For this, you can use getDataValidation [2] and getCriteriaValues [3] functions chained to a Range object to retrieve the current drop-down values array on that range and delete the option matching with the selected option.
Use newDataValidation() [4] function to create a new rule using your updated drop-down values array and setDataValidation [5] function to set the rule to the range.
function onEdit(event) {
var range = event.range;
var sheetName = range.getSheet().getSheetName();
var col = range.getColumn();
var newValue = event.value;
var oldValue = event.oldValue;
//If the edited range is in sheet '3W' and beetween columns I-L
if(sheetName == '3W') {
if(col>=9 && col<=12) {
for(var i=9; i<13; i++) {
//Don't change anything for edited cell
if(col == i) { continue; }
else {
//Get range to update and current dropdown values for that range
var rangeToUpdate = range.getSheet().getRange(range.getRow(), i, 1, 1);
var dropdownValues = rangeToUpdate.getDataValidation().getCriteriaValues()[0];
//Find new edited value and delete it from options array
var index = dropdownValues.indexOf(newValue);
if (index > -1) {
dropdownValues.splice(index, 1);
}
//If previous selected value is not beetween the options, add it
if(oldValue && dropdownValues.indexOf(oldValue) == -1) {
Logger.log(oldValue)
dropdownValues.push(oldValue);
}
//Set new dropdown values to range
var updatedRule = SpreadsheetApp.newDataValidation().requireValueInList(dropdownValues, true).setAllowInvalid(false);
rangeToUpdate.setDataValidation(updatedRule);
}
}
}
}
}
Run just the first time to set all the drop-downs in columns I-L, which are get it from range E1:E10:
function setDropdownsInitially() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Range with the dropdown values
var sheet = ss.getSheetByName("indicators");
var dropdownValues = sheet.getRange("E1:E10").getValues();
//Data validation rule
var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropdownValues, true).setAllowInvalid(false);
//Range where the dropdowns will be created
var targetSheet = ss.getSheetByName("3W");
var cells = targetSheet.getRange("I2:L");
//Set data validation rule
cells.setDataValidation(rule);
}
[1] https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
[2] https://developers.google.com/apps-script/reference/spreadsheet/range#getdatavalidation
[3] https://developers.google.com/apps-script/reference/spreadsheet/data-validation-builder.html#getcriteriavalues
[4] https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#newdatavalidation
[5] https://developers.google.com/apps-script/reference/spreadsheet/range#setdatavalidationrule
So I'm using this script (credit to Chicago Computer Classes) for populating dynamic data validation of a Google Sheets cell based on what the user entered in a different cell.
For example, if they enter the sport "Football" in one cell, the next cell has data validation for "CFB, CFL, or NFL" but if they enter "Basketball" in the first cell then the second cell's data validation changes to "ABL, CBB, NBA, or WNBA" for examples.
The script is working fantastic and you are welcome to play with the sheet here
However ... here's my problem:
I have an existing spreadsheet with 9000 rows of data. I would like to apply this new data validation scheme to this spreadsheet. The script is triggered with the onEdit() function which works great when you are entering things one row at a time. But if I try to copy and paste a whole bunch of rows in the first column, only the first row of the second column triggers the onEdit and gets the new data validation while all the other rows of the second column are unchanged. I've also tried to "Fill Down" or "Fill Range" on the first column and they have the same result where the first row in the selected range gets the new data validation but the rest of the selection is unchanged.
And while it would work just fine if I was manually entering rows, I really don't feel like doing that 9000 times :)
How do I modify the script to trigger the function with data that's copy/pasted or filled down?
Thanks!
Script here:
function onEdit(){
var tabLists = "Leagues";
var tabValidation = "2018";
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 6 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(0, 1).clearContent().clearDataValidations();
var makes = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) + 1;
if(makeIndex != 0){
var validationRange = datass.getRange(3, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
You should use the event object, which will provide you with the range that was edited. What you're doing now is looking only at the "active cell", which doesn't leverage the benefits of the event object, and can also lead to bugginess when you make rapid changes.
Using the event object, when you make an edit to multiple cells at once (from copy/paste), you can then loop through the range and set your validations.
function onEdit(e) {
var editedRange = e.range;
var ss = editedRange.getSheet();
var tabValidation = "2018";
if(editedRange.getColumn() == 6 && editedRange.getRow() > 1 && ss.getSheetName() == tabValidation) {
var tabLists = "Leagues";
var tabListsSheet = e.source.getSheetByName(tabLists);
var makes = tabListsSheet.getRange(1, 1, 1, tabListsSheet.getLastColumn()).getValues(); // This won't change during execution, so call only once
var activeCell = editedRange.getCell(1,1); // Start with the first cell
var remainingRows = editedRange.getHeight();
while(remainingRows > 0) {
var cellValue = activeCell.getValue();
activeCell.offset(0, 1).clearContent().clearDataValidations(); // Always clear content & validations
if (cellValue != "") { // Add validations if cell isn't blank
var makeIndex = makes[0].indexOf(cellValue) + 1;
if(makeIndex != 0) {
var validationRange = tabListsSheet.getRange(3, makeIndex, tabListsSheet.getLastRow()-2);
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
activeCell = activeCell.offset(1, 0); // Get the next cell down
remainingRows--; // Decrement the counter
}
}
}
We're trying to show a text box depending if somebodies row has "Y" in it. In column B we have a load of email addresses, Column C has emails, column D is empty and Column E defines manager or not ("Y").
What we're trying to do is show the text box if the signed in user has a Y in the adjoining column. However, everything we've tried seems to either always show the text box or never show the text box.
I'm only going to show the code for this particular function as there is approx 4000 lines over the who script.
This is the way we've got it so far. We think we're quite close (although we may not be!).
function vUser(app){
var userID = Session.getActiveUser();
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID2).getSheetByName("Managers");
var values = sheet.getRange(1,1,sheet.getLastRow(),13).getValues();
for (var i in values){
if ((values[i][0] == userID) && (values[i][2] == 'Y')){
var app = UiApp.getActiveApplication();
createForm_(app);
app.getElementById('manager').setVisible(true);
app.getElementById('managerTextBox').setVisible(true);
return app;
}else {
var app = UiApp.getActiveApplication();
createForm_(app);
continue; // entered this to try and make it read down. Now shows 30 of the generic questions as my username is on the 30th row. Does show the 'manager' and 'managertextbox' once, however.
return app; //removed when continue; is included.
}
}
}
how about something like:
for (var i in values){
if ((values[i][0] == userID) && (values[i][2] == 'Y')){
var is_manager = 1;
}}
if (is_manager ==1){
var app = UiApp.getActiveApplication();
createForm_(app);
app.getElementById('manager').setVisible(true);
app.getElementById('managerTextBox').setVisible(true);
return app;
}