How do I reveal a hidden row based on a cell value? - validation

I have created a sheet which has a dropdown with values "Select One:" "Push" "Pull" in cell B23. I would like row 24 to be hidden by default. Upon selection of the value "Pull" from the dropdown, I would like row 24 to be unhidden.
Is there a way to unhide row 24 if the selection in cell B23 = Pull?

You want to:
Show row 24 if dropdown in B23 is set to Pull.
Hide row 24 in any other case.
If that's the case, you can copy this simple onEdit trigger to the script bound to your spreadsheet (the sheet with the data is called Sheet1 in this sample, please change this accordingly if that's not the case):
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("Sheet1"); // Please change accordingly
var dropdown = sheet.getRange("B23").getValue();
var row = 24;
if (dropdown == "Pull") sheet.showRows(row);
else sheet.hideRows(row);
}
As you can see, there is no need to use a for loop, which will slow down the execution (specially considering this will run every time the file is edited).
In order to make sure row 24 will be hidden or shown accordingly the first time you open your file, you can attach an onOpen trigger to the function, like this:
function onOpen(e) {
onEdit(e)
}
Reference:
onEdit
onOpen
hideRows(rowIndex)
showRows(rowIndex)
I hope this is of any help.

use this script:
function onEdit(e)
{
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName("Sheet1");
var rg=sh.getDataRange();
var vA=rg.getValues();
var xx=sh.getRange("B23").getValue();
for(var i=0;i<vA.length;i++)
{
var row=i+1;
switch(xx)
{
case 'Pull':
if(row=24){sh.showRows(row);}
break;
case 'Select One:':
if(row=24){sh.hideRows(row);}
break;
case 'Push':
if(row=24){sh.hideRows(row);}
break;
default:
}
}
}

Related

onEdit trigger based on OUTCOME (VALUE) from formula result

I am trying to create an AUTOSORT onEdit() script that is only triggered when a value in Column D (4) matches a certain text. In this pattern, when the onEdit trigger becomes active, range.sort({column:11,ascending:true}) is used in the active sheet, but is limited to a preselected range of sheets.
I have a script for when a cell is changed completely, but unfortunately doesn't run when the value is changed due to a formula.
What I would like to have is a script that checks the outcome of a formula for a specific TEXT in Column D (4) whenever it is changed.
What I have so far (not working at this moment):
function onEdit(e) {
multiSortColumns(e);
}
function multiSortColumns(e) {
if (e.range.columnStart == 4 && e.range.getValue() == 'CHANGE OF DATE') {
var sheets = ["Sheet1", "Sheet2", , ,];
var sheet = e.range.getSheet();
if (sheets.includes(sheet.getSheetName())) {
var range = sheet.getRange("A5:bY600");
range.sort({ column: 11, ascending: true });
e.source.toast('Sort complete.');
}
}
}
Because the value in column D is not manually edited but through a formula or function, onEdit(e) doesn't consider that as an event as explained in the limitations below:
- Script executions and API requests do not cause triggers to run. For example, calling Range.setValue() to edit a cell does not cause the spreadsheet's onEdit trigger to run.
But let's say the cell that is manually edited is in column A and based on that, the cell value in the same row in column D changes to "CHANGE OF DATE", you can adjust your code by catching that edit event in column A :
function onEdit(e) {
multiSortColumns(e);
}
function multiSortColumns(e) {
//var refcell = SpreadsheetApp.getActiveSheet().getActiveCell();
if (e.range.columnStart == 1 && e.range.offset(0,3).getValue() == 'CHANGE OF DATE') { //i.e. col 1 is edited and col 1+3 shows "CHANGE OF DATE"
var sheets = ["Sheet1", "Sheet2", , ,];
var sheet = e.range.getSheet();
if (sheets.includes(sheet.getSheetName())) {
var range = sheet.getRange("A5:bY600");
range.sort({ column: 11, ascending: true });
e.source.toast('Sort complete.');
}
}
}
Your column D is still used as a pre-condition for sorting but using offset() relative to column A where the edit event actually happened.

Multiple dependent dynamic dropdowns with repeating column dropdowns in Google Sheets

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

Have Google Sheet Linked to Google Form Sort By Date Automatically

I use a Google Form for people to request days off. An add-on called Form Approvals is used to send emails to certain people who can approve or deny the request. In the Google Sheet listing the responses, new entries keep going to the bottom.
Is there a way to make new entries from the Google Form to be sorted automatically by the date of the day off in the Google Sheet?
I found this script, but it doesn't work:
function onEdit(event){
var sheet = event.source.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 2;
var tableRange = sheet.getDataRange();
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy } );
}
}
Also, is there a way to specify which sheet tab for the script to run on?
Try this:
function onEdit(e){
var sh=e.range.getSheet();
if(sh.getName()!="Your desired sheet name")return;
if(e.range.columnStart==2){
sh.getDataRange().sort({ column:2});
}
}
A lot of new programmers try to run these onEdit(e) functions from the script editor. Unfortunately, that doesn't work because the e parameter is expecting to be populated by the event trigger. Without the event object you'll normally get an error like Cannot read property range from undefined because e has not been populated by the event trigger.
I test them by making sure I'm editing the correct sheet and correct range and I use the e.source.toast() function to provide me with feed back sort of like the console.log() does.
If you want to learn more about the event object then try adding a Logger.log(JSON.stringify(e)); to the first line after the function declaration. And then get it to run by editing the appropriate sheet in the appropriate way and go to view log to see the results.
If your sheet is populated by a form and you want to sort the data every time a new form is submitted - you need to use the onFormSubmit trigger.
Google Forms populates the destination spreadsheet chronologically, in order to avoid interference you can use a sync sheet to which the data is transferred on every form submit and which you can sort as desired.
Sample:
function myFunction() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet=ss.getActiveSheet();
if(sheet.getName()=="Name of Tab to copy and sort"){
var lastRow=sheet.getLastRow();
var lastCol=sheet.getLastColumn();
var range=sheet.getRange(lastRow,1,1,lastCol);
var secondarySheetId="XXX";//Paste here the Id of the secondary spreadsheet
var secondarySheet=SpreadsheetApp.openById(secondarySheetId).getSheetByName("Name of tab of your choice");
secondarySheet.getRange(secondarySheet.getLastRow()+1,1,1,lastCol).setValues(range.getValues());
SpreadsheetApp.flush();
var secondaryRange = secondarySheet.getDataRange();
var columnToSortBy=2; //adapt to your needs
secondaryRange.sort( { column : columnToSortBy } );
}
}
Atach this script to the destination spreadsheet, insert the Id of a secondary spreadsheet (which you have to create first), save the script and bind an installable onFormSubmit trigger to the script through Edit->Current project's triggers->New trigger.
I figured it out using https://www.idiotinside.com/2018/06/08/sort-multiple-columns-google-sheets-apps-script/ :)
For my spreadsheet:
The name of the sheet I want to sort (not every sheet in the spreadsheet) is called "Requests".
Dates are in Column B which is column number 2.
Dates are sorted with oldest at the top.
Be sure to edit SHEET_NAME, SORT_DATA_RANGE, and SORT_ORDER to your needs.
Use this script with an onEdit trigger:
SHEET_NAME = "Requests";
SORT_DATA_RANGE = "A2:L1500";
SORT_ORDER = [
// {column: 1, ascending: true}, // 1 = column number for Column A, sorting by ascending order
// {column: 3, ascending: false}, // 3 = column number for Column C, sorting by descending order
{column: 2, ascending: true},
];
function onEdit(e){
multiSortColumns();
}
function multiSortColumns(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(SHEET_NAME);
var range = sheet.getRange(SORT_DATA_RANGE);
range.sort(SORT_ORDER);
ss.toast('Sorting by Date completed.');
}
Then go to https://script.google.com/ and create a trigger for the above script. Under "Select event type" use "On form submit".
This is working well so far :)

Applying Google Apps Script for Dynamic Data Validation to Existing Sheet

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

How to set default drop down list value from another drop down list in sheets?

Here is what I am trying to do. I have a list that populates a drop down list created by data validation. I then query the data to populate a second list based on the first drop down value. Everything works except I would like to set the first value in the second drop down to be the default value. In other words if I change the value in the first drop down my second drop down now shows an error until I select the drop down and change the value to a correct value. I would like to set it to the first correct value when the first drop down is changed. My reason for this is I dont want someone to forget to change the second drop down and get a value that is not even in the range.
Please help I am new to this kind of thing.
thanks
https://docs.google.com/spreadsheets/d/1U2_0Ku1bCLfDh_v2XyE9aQMSfdYmlVfX_8SKmQay48g/edit?usp=sharing
Here is what I have so far. It works except anytime the sheet is edited the value is reset. I only want it to reset when Cell A2 changes.
function onEdit() {
var ss = SpreadsheetApp.getActive()
var sheet = SpreadsheetApp.getActiveSheet();
// IF(cellContent has changed) then clear B2
// else do nothing.
sheet.getRange('B2').clearContent();
var value = sheet.getRange('H2').getValue();
sheet.getRange('B2').setValue(value);
}
I figured it out and this seems to work just fine.
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var cellR = cell.getRow();
var cellC = cell.getColumn();
var cellValue = cell.getValue();
var active_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var value = active_spreadsheet.getRange('H2').getValue();
if (cellR == 2 && cellC == 1) {
sheet.getRange('B2').clearContent();
sheet.getRange('B2').setValue(value);
}
}

Resources