Dependent Dropdowns: 1 Independent, multiple dependent - google-apps-script

So, this goal seemed simple at first, but I can't seem to wrap my head around how to accomplish it.
I have these independent dropdowns, with between 3 to 4 dependent dropdowns associated with each independent. I would like to be able to apply the associated dependent dropdowns when the independent dropdown is selected.
Example: Dropdowns in Column A have 3-4 variables associated with them (https://docs.google.com/spreadsheets/d/1ErRBqfEeRqTNLIH0N11IjViP-_ej1LMdrOXNvDOcsx8/edit?usp=sharing)
In the following code, I can apply 1 independent, and 1 dependent, but I can't figure out how to scale it to my needs:
function onEdit(){
var tabLists = "Lists";
var spreadsheet = SpreadsheetApp;
var activeSheet = spreadsheet.getActiveSpreadsheet().getActiveSheet();
var data = spreadsheet.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = activeSheet.getActiveCell();
if(activeCell.getColumn() == 1 && activeCell.getRow() > 1 && activeSheet.getSheetName().includes("Programming")){
activeCell.offset(0, 1).clearDataValidations(); //Clears data validation 1 column over
var exerciseCat = data.getRange(1, 1, 1, data.getLastColumn()).getValues();
var catIndex = exerciseCat[0].indexOf(activeCell.getValue()) + 1;
if(catIndex != 0){
var validationRange = data.getRange(3, catIndex, data.getLastRow());
var validationRule = spreadsheet.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
I thought that maybe my answer existed within making a 4-level dependent dropdown, but that didn't work either.
All help is greatly appreciated, as I am still quite new to any form of coding.

From your showing script, I guessed that you might have wanted to directly run the script with the script editor. So, in this modification, I didn't use the event object. By this, you can run the script with the OnEdit trigger and also directly run this modified script with the script editor.
And, in your script, only the data validation of column "B" of "Programming" sheet is set. In order to set other columns, I prepared an object for retrieving the values for setting the data validation rules.
So, how about the following modification?
Sample script:
function onEdit() {
var tabLists = "Lists";
var spreadsheet = SpreadsheetApp;
var activeSheet = spreadsheet.getActiveSpreadsheet().getActiveSheet();
var data = spreadsheet.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = activeSheet.getActiveCell();
// I modified below script.
var row = activeCell.getRow();
if (activeCell.getColumn() == 1 && row > 1 && activeSheet.getSheetName().includes("Programming")) {
var obj = { "Top Sets": [1, 2, 3], "Top Set/Percentage": [4, 5, 6, 7] };
var v = activeCell.getValue();
if (!obj[v]) return;
activeCell.offset(0, 1, 1, activeSheet.getLastColumn() - 1).clearContent().clearDataValidations();
var lastRow = data.getLastRow();
var rules = obj[v].map(r => {
var r = data.getRange(3, r, lastRow - 2, 1);
return SpreadsheetApp.newDataValidation().requireValueInRange(r).build();
});
activeCell.offset(0, 1, 1, rules.length).clearContent().setDataValidations([rules]);
}
}
When the column "A" of "Programming" sheet is edited, this script is run. And, by the value of column "A", the data validation rules of other columns are put.
Note:
This modified script can be tested using your provided Spreadsheet. So, when you change the Spreadsheet, this script might not be able to be used. Please be careful about this.

Related

Run Google appscript on change of dropdonw

I am very new to Google Appscript and support of this community will really be appreciated. Using different posts on the Stakeoverflow community I had designed an appscript to copy and paste the data from one worksheet to another and this is working fine (the data of worksheet named Copy is paste in worksheet named Paste). I had also created a worksheet named Trigger and in that I had created a dropdown in cell A1, now I want my script to run every time when the dropdonw in this sheet is changed. I had checked the different solutions on this community but as am new to appscirpt could not design a perfect solution to my case. Below is the link to the sheet and my current script.
https://docs.google.com/spreadsheets/d/1xy5eN8_PHXi9RPWK_EKg99dylaDjQ9lcilrSW0GP4B0/edit#gid=0
function Referesh() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2,1,copySheet.getLastRow(),2);
var rub = copySheet.getRange(2,1,copySheet.getLastRow(),2);
var destination =
pasteSheet.getRange(pasteSheet.getLastRow()+1,2,copySheet.getLastRow(),2);
source.copyTo((destination), {contentsOnly: true});
rub.clearContent();
console.log(pasteSheet.getLastRow());
}
Any help on above will be appreciated.
I believe your goal is as follows.
You want to automatically run your script by changing the dropdown list of the cell "A1" of "Trigger" sheet.
In this case, how about using the OnEdit simple trigger as follows?
Modified script:
Please copy and paste the following script to the script editor of Spreadsheet and save the script. When you use this script, in this case, please change the dropdown list of the cell "A1" of "Trigger" sheet to "Run". By this, the script is run.
function onEdit(e) {
var sheetName = "Trigger";
var runScript = "Run";
var { range, source, value } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 1 || range.rowStart != 1 || value != runScript) return;
var ss = source;
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var rub = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var destination = pasteSheet.getRange(pasteSheet.getLastRow() + 1, 2, copySheet.getLastRow(), 2);
source.copyTo((destination), { contentsOnly: true });
rub.clearContent();
console.log(pasteSheet.getLastRow());
range.setValue("Refresh"); // By this script, the value of "Run" is changed to "Refresh".
}
Note:
It seems that there are 2 values of Run and Refresh in your dropdown list. Unfortunately, I couldn't know the trigger value. So, in this sample script, when the dropdown list is changed to Run, the script is run by the simple trigger. If you want to change this, please modify the above script.
When you run directly onEdit with the script editor, an error occurs because of no event object. So, please be careful about this.
If you want to ignore the trigger value, I think that the following script might be able to be used. In this case, the dropdown list is changed, the script is run.
function onEdit(e) {
var sheetName = "Trigger";
var { range, source } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 1 || range.rowStart != 1) return;
var ss = source;
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var rub = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var destination = pasteSheet.getRange(pasteSheet.getLastRow() + 1, 2, copySheet.getLastRow(), 2);
source.copyTo((destination), { contentsOnly: true });
rub.clearContent();
console.log(pasteSheet.getLastRow());
}
Reference:
Simple Triggers

Moving Rows from one sheet to another based on selected values

I have an Apps Script I am using to move rows from one tab on my sheet to another based on what I select in a specific column.
The code I am using seems to work however it is putting two copies of the line on the second sheet I am pretty new to app script so i may have made an error in the code. I litterally just need the row to move over and delete from the original page.
function onEdit(e){
var sourceSheet = e.range.getSheet();
var row = e.range.getRow();
if(sourceSheet.getSheetName() === 'Submissions'){
var rowRange = sourceSheet.getRange(row, 1, 1, sourceSheet.getLastColumn());
var rowValues = rowRange.getValues()[0];
if(rowValues[16] === "Approved"){
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Completed");
targetSheet.appendRow(rowValues);
sourceSheet.deleteRow(row);
}
}
}
Delete Selected Rows
function onEdit(e) {
var sh = e.range.getSheet();
if(sh.getName() == 'Submissions'){
var rowRange = sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn());
var rowValues = rowRange.getValues()[0];
if(rowValues[16] == "Approved"){
var tsh = e.source.getSheetByName("Completed");
tsh.appendRow(rowValues);
sh.deleteRow(e.range.rowStart);
}
}
}
Perhaps you create and installable trigger with the name onEdit. In this case you are getting two triggers. One the installable version and one from the simple trigger. I also rewrote the function taking advantage of event object.

How to change an onEdit function to work whenever a script runs? - Google Sheets

I have written an onEdit function, which creates a dropdown list which changes depending on the adjacent value entered into column G of the same row.
The code is:
function onEdit() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();;
var OEsheet = ss.getSheetByName("OE names");
var SOsheet = ss.getSheetByName("Sales Order");
var activeCell = SOsheet.getActiveCell();
if(activeCell.getColumn() == 7 && activeCell.getRow() > 1 && ss.getSheetName() == "Sales Order") {
activeCell.offset(0, -6).clearContent().clearDataValidations();
var OEnames = OEsheet.getRange(1, 1, 1, OEsheet.getLastColumn()).getValues();
var OEnamesIndex = OEnames[0].indexOf(activeCell.getValue()) + 1;
if(OEnamesIndex != 0){
var validationRange = OEsheet.getRange(2, OEnamesIndex, OEsheet.getLastRow());
var validationRule = app.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, -6).setDataValidation(validationRule);
}
}
}
This script works perfectly, but the problem is the data added to column G for my spreadsheet will be added by a script, not manually so I have opted to merge this function with another function so it triggers the creation of the data validation for the whole of Column A when the previous script is triggered, rather than on a manual edit. Therefore, I changed the code to this (which I will merge with a previous function):
function DropDowns() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();;
var OEsheet = ss.getSheetByName("OE names");
var SOsheet = ss.getSheetByName("Sales Order");
var DDcolumn = SOsheet.getRange("A2:A");
var Bcolumn = SOsheet.getRange("G2:G");
Bcolumn.clearContent().clearDataValidations();
var OEnames = OEsheet.getRange(1, 1, 1, OEsheet.getLastColumn()).getValues();
var OEnamesIndex = OEnames[0].indexOf(Bcolumn.getValue()) + 1;
if(OEnamesIndex != 0){
var validationRange = OEsheet.getRange(2, OEnamesIndex, OEsheet.getLastRow());
var validationRule = app.newDataValidation().requireValueInRange(validationRange).build();
DDcolumn.setDataValidation(validationRule);
}
}
However, this does not work properly - it creates a data validation for the whole column (which is what I wanted), but it does not use the value in the adjacent column of the same row to create this data validation (because I had to remove the activeCell functionality as the data will not be manually entered I think). It now seems to be using only Column A in the OE names sheet to create the data validation for the row, rather than the column in the OE sheet which corresponds to the value in column G of the Sales Orders sheet.
This is a link to a copy of my spreadsheet with all the irrelevant scripts and data removed: https://docs.google.com/spreadsheets/d/1bligSkSDr0dtU3Zwj1c-SasvKbHqX-QhbM8zysIvM-Q/edit?usp=sharing
How can I fix this so that the script runs and adds dropdown lists to the whole column, but the content of these lists still change depending on the adjacent value in column G?
Thank you

How to add new rows when archiving a row in Google Sheets

I currently have working code inside of my Google Sheet. The code moves certain rows in the sheet over to another sheet when labeled as "Archive" in a drop down menu.
The problem I have is that when this happens, the entire row gets deleted. I only need the information from column C:O (C2:O) to be archived. Another problem that this creates, when it deletes the row the other rows move up, thus deleting the set amount of rows I have created for input.
I need it to automatically replace the archive rows with another row with all the same conditional formatting and functions so that it does not disrupt the rest of the sheet and there is no need to go in and manually create more rows.
Please help, thank you very much in advance.
Current code used in Google App Scripts is attached below.
function myFunction() {
// moves a row from a sheet to another when a magic value is entered in a column
// adjust the following variables to fit your needs
// see https://productforums.google.com/d/topic/docs/ehoCZjFPBao/discussion
var sheetNameToWatch = 'Campaigns';
var columnNumberToWatch = 6;
// column A = 1, B = 2, etc…
var valueToWatch = 'Archive';
var sheetNameToMoveTheRowTo = 'Archive';
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && range.getValue() == valueToWatch) {
var targetSheet = ss.getSheetByName(sheetNameToMoveTheRowTo);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
}
}
You just need to use copyTo() [1] function instead of moveTo() [2] function which is cutting and pasting the source row. Also, you need to remove the deleteRow function if you just want to leave the row cells with same format and functions set. You only would need to change this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).moveTo(targetRange);
sheet.deleteRow(range.getRow());
To this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).copyTo(targetRange);
Moreover, if you just want to copy the values and not the formulas from the cells to the target sheet, use the copyTo function with options [3], like this:
sheet.getRange(range.getRow(), 1, 1, sheet.getLastColumn()).copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
[1] https://developers.google.com/apps-script/reference/spreadsheet/range#copytodestination
[2] https://developers.google.com/apps-script/reference/spreadsheet/range#movetotarget
[3] https://developers.google.com/apps-script/reference/spreadsheet/range#copytodestination-copypastetype-transposed
If you want to clear the values without deleting the row, you'll want to change your approach in the following ways:
obtain a Range which includes the data you want to archive
to populate the archive sheet, use copyTo() instead of moveTo()
obtain a Range which includes only the cells you want to clear out
use range.clearContent() to clear that range
Here's a reimplementation of your function. I've extracted the configurable bits into global variables, which makes adjusting them down the line a bit easier to do.
var SOURCE_SHEET_NAME = 'Campaigns';
var TARGET_SHEET_NAME = 'Archive Campaigns';
// The cell value that will trigger the archiving action.
var ARCHIVE_VALUE = 'Archive';
// The column number where you expect the ARCHIVE_VALUE to appear.
var ARCHIVE_COLUMN_NUMBER = 4;
// The starting and ending columns for the range of cells to archive.
var ARCHIVE_START_COLUMN = 1;
var ARCHIVE_END_COLUMN = 15;
// The starting and ending columns for the range of cells to clear.
var CLEAR_START_COLUMN = 1;
var CLEAR_END_COLUMN = 10;
function archiveRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var activeCellRange = sheet.getActiveCell();
if (sheet.getName() != SHEET_NAME ||
activeCellRange.getColumn() != ARCHIVE_COLUMN_NUMBER ||
activeCellRange.getValue() != ARCHIVE_VALUE) {
return;
}
var activeCellRow = activeCellRange.getRow();
// Get the range for the data to archive.
var archiveNumColumns = ARCHIVE_END_COLUMN - ARCHIVE_START_COLUMN + 1;
var archiveRowRange = sheet.getRange(activeCellRow, ARCHIVE_START_COLUMN, 1, archiveNumColumns)
// Get the range for the archive destination.
var targetSheet = ss.getSheetByName(TARGET_SHEET_NAME);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1, 1, archiveNumColumns);
// Copy the data to the target range.
archiveRowRange.copyTo(targetRange);
// Get the range for the data to clear.
var clearNumColumns = CLEAR_END_COLUMN - CLEAR_START_COLUMN + 1;
var clearRowRange = sheet.getRange(activeCellRow, CLEAR_START_COLUMN, 1, clearNumColumns);
// Clear the row.
clearRowRange.clearContent();
}

Data Validation between workbooks in Google sheets

I've been having some trouble moving data from one source Spreadsheet to another, and having my formulas work. An overview of what I'm trying to do is have Spreadsheet 2 (the active document) pull information from Spreadsheet 1 (the source document). The code I made and was using before looked into a data validated dropdown list and then auto populated a new dropdown list. I want to automate the 1st data validated column and then have the same code run. I've put the code I'm having trouble with below. I am pretty new to this so any help would be much appreciated.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.openById("1APLcwkIE2EqVSS6Zongy799yg4z2tAN9J7h3jlJoBgQ").getSheetByName("Master List");
var activeCell = ss.getActiveCell();
var makes = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) + 1;
if(activeCell.getColumn() == 1 && activeCell.getRow() > 1) {
activeCell.offset(0, 1).clearContent().clearDataValidations();
if(makeIndex != 0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
} }
So, I didn't find any script very easily but I found an add on that helped so much with this! Use SheetGo for anyone who has a similar issue, if your looking for an easy answer!