Как вы делаете динамические / зависимые выпадающие списки в Google Таблицах?
Как вы получаете столбец подкатегорий для заполнения выпадающего списка на основе значения, выбранного в раскрывающемся списке основная категория в Google Таблицах?
я погуглил и не мог найти хороших решений, поэтому я хотел поделиться своими собственными. Пожалуйста, смотрите мой ответ ниже.
5 ответов
вы можете начать с листа google, настроенного с главной страницы и выпадающей исходной страницы, как показано ниже.
вы можете настроить выпадающий первый столбец через обычные данные > подсказки меню проверки.
Главная Страница
Выпадающая Исходная Страница
после этого вам нужно настроить скрипт на имя onEdit
. (Если вы не используете это имя, getActiveRange () не будет делать ничего, кроме возврата ячейки A1)
и используйте код, указанный здесь:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var myRange = SpreadsheetApp.getActiveRange();
var dvSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Categories");
var option = new Array();
var startCol = 0;
if(sheet.getName() == "Front Page" && myRange.getColumn() == 1 && myRange.getRow() > 1){
if(myRange.getValue() == "Category 1"){
startCol = 1;
} else if(myRange.getValue() == "Category 2"){
startCol = 2;
} else if(myRange.getValue() == "Category 3"){
startCol = 3;
} else if(myRange.getValue() == "Category 4"){
startCol = 4;
} else {
startCol = 10
}
if(startCol > 0 && startCol < 10){
option = dvSheet.getSheetValues(3,startCol,10,1);
var dv = SpreadsheetApp.newDataValidation();
dv.setAllowInvalid(false);
//dv.setHelpText("Some help text here");
dv.requireValueInList(option, true);
sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).setDataValidation(dv.build());
}
if(startCol == 10){
sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).clearDataValidations();
}
}
}
после этого настройте триггер на экране редактора сценариев, перейдя в меню Правка > текущие триггеры проекта. Это вызовет окно, чтобы вы выбрали различные выпадающие списки, чтобы в конечном итоге оказаться на этом:
вы должны быть хорошо идти после этого!
Примечание
скрипты имеют ограничение: он обрабатывает до 500 значений в одном раскрывающемся списке.
Новый Сценарий. 201801
сценарий был выпущен в январе 2018. Пожалуйста, смотрите:
- главная страница с инструкцией и демо, где вы можете задать вопрос.
- страница GitHub с инструкциями и источник код.
улучшения:
- скорость
- обрабатывает несколько правил в 1 листе
- связать другие листы в качестве исходных данных.
- пользовательский порядок столбцов раскрывающихся списков
Старый Сценарий.
версии скрипта
- В. 1.
- В. 2. 2016-03. Улучшено: работает с дубликатами в любом категория. Например, если у нас есть list1 с моделями автомобилей и list2 с цветами. Цвет можно повторить в любой модели.
- В3. 2017-01. Улучшено: нет ошибки при вводе единственного значения.
- новая версия: 2018-02. См.вот эту статью.
это решение не идеально, но оно дает некоторые преимущества:
- позвольте вам сделать несколько выпадающих списки
- дает больше контроля
- исходные данные размещаются на единственном листе, поэтому их легко редактировать
прежде всего, вот пример работающего, так что вы можете проверить его, прежде чем идти дальше.
мой план:
- Подготовка Данных
- сделать первый список как обычно:
Data > Validation
- добавить скрипт, установить некоторые переменные
- готово!
Подготовка Данных
данные выглядят как одна таблица со всеми возможными вариантами внутри нее. Он должен быть расположен на отдельном листе, поэтому его можно использовать скрипт. Посмотрите на этот пример:
здесь у нас есть четыре уровня, каждому значению повторяется. Обратите внимание, что 2 столбца справа от данных зарезервированы, поэтому не вводите / не вставляйте их данные.
первая простая проверка данных (DV)
подготовьте список уникальных значений. В нашем примере это список планеты. Найдите свободное место на листе с данными и вставьте формулу:=unique(A:A)
На главном листе выберите первый столбец, с которого начнется DV. Перейдите в раздел Данные > проверка и выберите диапазон с уникальным списком.
скрипт
вставить этот код в Редакторе скриптов:
function SmartDataValidation(event)
{
//--------------------------------------------------------------------------------------
// The event handler, adds data validation for the input parameters
//--------------------------------------------------------------------------------------
// Declare some variables:
//--------------------------------------------------------------------------------------
var TargetSheet = 'Main' // name of the sheet where you want to verify the data
var LogSheet = 'Data' // name of the sheet with information
var NumOfLevels = 4 // number of associated drop-down list levels
var lcol = 2; // number of the leftmost column, in which the changes are checked; A = 1, B = 2, etc.
var lrow = 2; // line number from which the rule will be valid
//--------------------------------------------------------------------------------------
// =================================== key variables =================================
//
// ss sheet we change (TargetSheet)
// br range to change
// scol number of column to edit
// srow number of row to edit
// CurrentLevel level of drop-down, which we change
// HeadLevel main level
// r current cell, which was changed by user
// X number of levels could be checked on the right
//
// ls Data sheet (LogSheet)
//
// ======================================================================================
// [ 01 ].Track sheet on which an event occurs
var ts = event.source.getActiveSheet();
var sname = ts.getName();
if (sname == TargetSheet)
{
// ss -- is the current book
var ss = SpreadsheetApp.getActiveSpreadsheet();
// [ 02 ]. If the sheet name is the same, you do business...
var ls = ss.getSheetByName(LogSheet); // data sheet
// [ 03 ]. Determine the level
//-------------- The changing sheet --------------------------------
var br = event.source.getActiveRange();
var scol = br.getColumn(); // the column number in which the change is made
var srow = br.getRow() // line number in which the change is made
// Test if column fits
if (scol >= lcol)
{
// Test if row fits
if (srow >= lrow)
{
var CurrentLevel = scol-lcol+2;
// adjust the level to size of
// range that was changed
var ColNum = br.getLastColumn() - scol + 1;
CurrentLevel = CurrentLevel + ColNum - 1;
// also need to adjust the range 'br'
if (ColNum > 1)
{
br = br.offset(0,ColNum-1);
} // wide range
var HeadLevel = CurrentLevel - 1; // main level
// split rows
var RowNum = br.getLastRow() - srow + 1;
var X = NumOfLevels - CurrentLevel + 1;
// the current level should not exceed the number of levels, or
// we go beyond the desired range
if (CurrentLevel <= NumOfLevels )
{
// determine columns on the sheet "Data"
var KudaCol = NumOfLevels + 2
var KudaNado = ls.getRange(1, KudaCol);
var lastRow = ls.getLastRow(); // get the address of the last cell
var ChtoNado = ls.getRange(1, KudaCol, lastRow, KudaCol);
// ============================================================================= > loop >
for (var j = 1; j <= RowNum; j++)
{
for (var k = 1; k <= X; k++)
{
HeadLevel = HeadLevel + k - 1; // adjust parent level
CurrentLevel = CurrentLevel + k - 1; // adjust current level
var r = br.getCell(j,1).offset(0,k-1,1);
var SearchText = r.getValue(); // searched text
// if anything is choosen!
if (SearchText != '')
{
//-------------------------------------------------------------------
// [ 04 ]. define variables to costumize data
// for future data validation
//--------------- Sheet with data --------------------------
// combine formula
// repetitive parts
var IndCodePart = 'INDIRECT("R1C' + HeadLevel + ':R' + lastRow + 'C';
IndCodePart = IndCodePart + HeadLevel + '",0)';
// the formula
var code = '=UNIQUE(INDIRECT("R" & MATCH("';
code = code + SearchText + '",';
code = code + IndCodePart;
code = code + ',0) & "C" & "' + CurrentLevel
code = code + '" & ":" & "R" & COUNTIF(';
code = code + IndCodePart;
code = code + ',"' + SearchText + '") + MATCH("';
code = code + SearchText + '";';
code = code + IndCodePart;
code = code + ',0) - 1';
code = code + '& "C" & "' ;
code = code + CurrentLevel + '",0))';
// Got it! Now we have to paste formula
KudaNado.setFormulaR1C1(code);
// get required array
var values = [];
for (var i = 1; i <= lastRow; i++)
{
var currentValue = ChtoNado.getCell(i,1).getValue();
if (currentValue != '')
{
values.push(currentValue);
}
else
{
var Variants = i-1; // number of possible values
i = lastRow; // exit loop
}
}
//-------------------------------------------------------------------
// [ 05 ]. Build daya validation rule
var cell = r.offset(0,1);
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(values, true)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
if (Variants == 1)
{
cell.setValue(KudaNado.getValue());
} // the only value
else
{
k = X+1;
} // stop the loop through columns
} // not blanc cell
else
{
// kill extra data validation if there were
// columns on the right
if (CurrentLevel <= NumOfLevels )
{
for (var i = 1; i <= NumOfLevels; i++)
{
var cell = r.offset(0,i);
// clean
cell.clear({contentsOnly: true});
// get rid of validation
cell.clear({validationsOnly: true});
}
} // correct level
} // empty row
} // loop by cols
} // loop by rows
// ============================================================================= < loop <
} // wrong level
} // rows
} // columns...
} // main sheet
}
function onEdit(event)
{
SmartDataValidation(event);
}
вот набор переменных, которые должны быть изменены, вы найдете их в скрипт:
var TargetSheet = 'Main' // name of the sheet where you want to verify the data
var LogSheet = 'Data' // name of the sheet with information
var NumOfLevels = 4 // number of associated drop-down list levels
var lcol = 2; // leftmost column, in which the changes are checked; A = 1, B = 2, etc.
var lrow = 2; // line number from which the rule will be valid
Я предлагаю всем, кто хорошо знает скрипты, отправить свои изменения в этот код. Я думаю, есть более простой способ найти список проверки и заставить скрипт работать быстрее.
Edit: ответ ниже может быть удовлетворительным, но у него есть некоторые недостатки:
существует заметная пауза для запуска сценария. У меня задержка 160 МС, и этого достаточно, чтобы раздражать.
он работает, создавая новый диапазон каждый раз, когда вы редактируете заданную строку. Это дает "недопустимое содержимое" для предыдущих записей иногда
Я надеюсь, что другие могут это убирать отчасти.
вот еще один способ сделать это, что экономит вам кучу имен диапазон:
три листа на листе: назовите их Main, List и DRange (для динамического диапазона.) На главном листе столбец 1 содержит временную метку. Этот штамп времени изменен onEdit.
в списке ваши категории и подкатегории расположены как простой список. Я использую это для инвентаризации растений на моей ферме деревьев, поэтому мой список выглядит так:
Group | Genus | Bot_Name
Conifer | Abies | Abies balsamea
Conifer | Abies | Abies concolor
Conifer | Abies | Abies lasiocarpa var bifolia
Conifer | Pinus | Pinus ponderosa
Conifer | Pinus | Pinus sylvestris
Conifer | Pinus | Pinus banksiana
Conifer | Pinus | Pinus cembra
Conifer | Picea | Picea pungens
Conifer | Picea | Picea glauca
Deciduous | Acer | Acer ginnala
Deciduous | Acer | Acer negundo
Deciduous | Salix | Salix discolor
Deciduous | Salix | Salix fragilis
...
где | указывает на разделение на столбцы.
Для удобства я также использовал заголовки в качестве имен для именованных диапазонов.
DRrange A1 имеет формулу
=Max(Main!A2:A1000)
Это возвращает самую последнюю метку времени.
А2 до А4 есть варианты:
=vlookup($A,Inventory!$A:$E00,2,False)
С увеличением 2 для каждой ячейки справа.
при запуске A2-A4 будет иметь выбранную в настоящее время группу, род и вид.
под каждым из них находится фильтр команда что-то вроде этого:
=unique (filter (Bot_Name, REGEXMATCH (Bot_Name, C1)))
эти фильтры заполнят блок ниже соответствующими записями для содержимого верхней ячейки.
фильтры могут быть изменены в соответствии с вашими потребностями и форматом вашего списка.
Back to Main: проверка данных в Main выполняется с использованием диапазонов от DRange.
скрипт, который я использую:
function onEdit(event) {
//SETTINGS
var dynamicSheet='DRange'; //sheet where the dynamic range lives
var tsheet = 'Main'; //the sheet you are monitoring for edits
var lcol = 2; //left-most column number you are monitoring; A=1, B=2 etc
var rcol = 5; //right-most column number you are monitoring
var tcol = 1; //column number in which you wish to populate the timestamp
//
var s = event.source.getActiveSheet();
var sname = s.getName();
if (sname == tsheet) {
var r = event.source.getActiveRange();
var scol = r.getColumn(); //scol is the column number of the edited cell
if (scol >= lcol && scol <= rcol) {
s.getRange(r.getRow(), tcol).setValue(new Date());
for(var looper=scol+1; looper<=rcol; looper++) {
s.getRange(r.getRow(),looper).setValue(""); //After edit clear the entries to the right
}
}
}
}
оригинальная презентация Youtube, которая дал мне большую часть компонента onedit timestamp: https://www.youtube.com/watch?v=RDK8rjdE85Y
здесь у вас есть другое решение, основанное на том, которое предоставлено @tarheel
function onEdit() {
var sheetWithNestedSelectsName = "Sitemap";
var columnWithNestedSelectsRoot = 1;
var sheetWithOptionPossibleValuesSuffix = "TabSections";
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = SpreadsheetApp.getActiveSheet();
// If we're not in the sheet with nested selects, exit!
if ( activeSheet.getName() != sheetWithNestedSelectsName ) {
return;
}
var activeCell = SpreadsheetApp.getActiveRange();
// If we're not in the root column or a content row, exit!
if ( activeCell.getColumn() != columnWithNestedSelectsRoot || activeCell.getRow() < 2 ) {
return;
}
var sheetWithActiveOptionPossibleValues = activeSpreadsheet.getSheetByName( activeCell.getValue() + sheetWithOptionPossibleValuesSuffix );
// Get all possible values
var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues.getSheetValues( 1, 1, -1, 1 );
var possibleValuesValidation = SpreadsheetApp.newDataValidation();
possibleValuesValidation.setAllowInvalid( false );
possibleValuesValidation.requireValueInList( activeOptionPossibleValues, true );
activeSheet.getRange( activeCell.getRow(), activeCell.getColumn() + 1 ).setDataValidation( possibleValuesValidation.build() );
}
Он имеет некоторые преимущества перед другим подходом:
- вам не нужно редактировать скрипт каждый раз, когда вы добавить "корень"вариант. Вам нужно только создать новый лист с вложенными параметрами этого корневого параметра.
- я переработал скрипт, предоставляющий больше семантических имен для переменных и так далее. Кроме того, я извлек некоторые параметры переменные чтобы было легче адаптироваться к вашему конкретному случаю. Вам нужно только установить первые 3 значения.
- нет предела вложенных значений опций (я использовал метод getSheetValues со значением -1).
Итак, как использовать:
- создайте лист, где у вас будут вложенные селекторы
- перейдите в "инструменты" > "редактор сценариев..." и выберите опцию "пустой проект"
- вставьте код, прикрепленный к этому ответ
- измените первые 3 переменные скрипта, настроив свои значения и сохраните его
- создать лист в этом же документе для каждого возможного значения "корень селектор". Они должны быть названы как значение + указанный суффикс.
наслаждайтесь!
продолжая эволюцию этого решения, я повысил ставку, добавив поддержку нескольких корневых выборок и более глубоких вложенных выборок. Это дальнейшее развитие решения JavierCane (которое, в свою очередь, построено на tarheel).
/**
* "on edit" event handler
*
* Based on JavierCane's answer in
*
* http://stackoverflow.com/questions/21744547/how-do-you-do-dynamic-dependent-drop-downs-in-google-sheets
*
* Each set of options has it own sheet named after the option. The
* values in this sheet are used to populate the drop-down.
*
* The top row is assumed to be a header.
*
* The sub-category column is assumed to be the next column to the right.
*
* If there are no sub-categories the next column along is cleared in
* case the previous selection did have options.
*/
function onEdit() {
var NESTED_SELECTS_SHEET_NAME = "Sitemap"
var NESTED_SELECTS_ROOT_COLUMN = 1
var SUB_CATEGORY_COLUMN = NESTED_SELECTS_ROOT_COLUMN + 1
var NUMBER_OF_ROOT_OPTION_CELLS = 3
var OPTION_POSSIBLE_VALUES_SHEET_SUFFIX = ""
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet()
var activeSheet = SpreadsheetApp.getActiveSheet()
if (activeSheet.getName() !== NESTED_SELECTS_SHEET_NAME) {
// Not in the sheet with nested selects, exit!
return
}
var activeCell = SpreadsheetApp.getActiveRange()
// Top row is the header
if (activeCell.getColumn() > SUB_CATEGORY_COLUMN ||
activeCell.getRow() === 1 ||
activeCell.getRow() > NUMBER_OF_ROOT_OPTION_CELLS + 1) {
// Out of selection range, exit!
return
}
var sheetWithActiveOptionPossibleValues = activeSpreadsheet
.getSheetByName(activeCell.getValue() + OPTION_POSSIBLE_VALUES_SHEET_SUFFIX)
if (sheetWithActiveOptionPossibleValues === null) {
// There are no further options for this value, so clear out any old
// values
activeSheet
.getRange(activeCell.getRow(), activeCell.getColumn() + 1)
.clearDataValidations()
.clearContent()
return
}
// Get all possible values
var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues
.getSheetValues(1, 1, -1, 1)
var possibleValuesValidation = SpreadsheetApp.newDataValidation()
possibleValuesValidation.setAllowInvalid(false)
possibleValuesValidation.requireValueInList(activeOptionPossibleValues, true)
activeSheet
.getRange(activeCell.getRow(), activeCell.getColumn() + 1)
.setDataValidation(possibleValuesValidation.build())
} // onEdit()
Как говорит Хавьер:
- создайте лист, где у вас будут вложенные селекторы
- перейдите в "инструменты" > "редактор сценариев..." и выберите " пустой проект" вариант
- вставьте код, прикрепленный к этому ответу
- измените константы в верхней части скрипта, настраивая свои значения и сохранить его
- создать один лист в этом же документе для каждого возможного значения "корневой селектор". Они должны быть названы значения указанного суффикс.
и если вы хотите увидеть его в действии, я создал демо-листа и вы можете увидеть код, если вы возьмете копию.