Я пытался добавить зависимое выпадающее меню 3-го и 4-го уровня, используя код из Code with Curt (https://codewithcurt.com/create-dependent-drop-down-on-google-web-app/), но я сталкиваюсь с некоторыми проблемами. В приведенном ниже коде я пытаюсь добавить 3-й уровень, но, похоже, это не работает. Это результат, которого я пытаюсь достичь. Я не уверен, есть ли самый быстрый способ загрузить раскрывающийся список из листов Google, так как этот код загружается примерно за 3 секунды, или лучший способ получить его из листов.
Вот код:
Скрипт Google Apps:
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile('DependentSelect');
var colors = getColors();
htmlOutput.message = '';
htmlOutput.colors = colors;
return htmlOutput.evaluate();
}
function doPost(e) {
Logger.log(JSON.stringify(e));
var name = e.parameters.name.toString();
var color = e.parameters.color.toString();
var fruit = e.parameters.fruit.toString();
var class = e.parameters.class.toString(); //class is a reserved word
AddRecord(name, color, fruit, class);
var htmlOutput = HtmlService.createTemplateFromFile('DependentSelect');
var colors = getColors();
htmlOutput.message = 'Record Added';
htmlOutput.colors = colors;
return htmlOutput.evaluate();
}
function getColors() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for (var i = 2; i <= getLastRow; i++) {
if (return_array.indexOf(lovSheet.getRange(i, 1).getValue()) === -1) {
return_array.push(lovSheet.getRange(i, 1).getValue());
}
}
return return_array;
}
function getFruits(color) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for (var i = 2; i <= getLastRow; i++) {
if (lovSheet.getRange(i, 1).getValue() === color) {
return_array.push(lovSheet.getRange(i, 2).getValue());
}
}
return return_array;
}
function getClass(fruit) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for (var i = 2; i <= getLastRow; i++) {
if (lovSheet.getRange(i, 2).getValue() === fruit) {
return_array.push(lovSheet.getRange(i, 3).getValue());
}
}
return return_array.sort();
}
function AddRecord(name, color, fruit, class) {
var url = ''; //URL OF GOOGLE SHEET;
var ss = SpreadsheetApp.openByUrl(url);
var dataSheet = ss.getSheetByName("DATA");
dataSheet.appendRow([name, color, fruit, class, new Date()]);
}
function getUrl() {
var url = ScriptApp.getService().getUrl();
return url;
}
HTML:
<!DOCTYPE html>
<html>
<head>
<base target = "_top">
</head>
<body>
<script>
function GetFruit(color)
{
google.script.run.withSuccessHandler(function(ar)
{
console.info(ar);
fruit.length = 0;
let option = document.createElement("option");
option.value = "";
option.text = "";
fruit.appendChild(option);
ar.forEach(function(item, index)
{
let option = document.createElement("option");
option.value = item;
option.text = item;
fruit.appendChild(option);
});
}).getFruits(color);
};
function getClass(queue)
{
google.script.run.withSuccessHandler(function(ar)
{
console.info(ar);
class.length = 0;
let option = document.createElement("option");
option.value = "";
option.text = "";
class.appendChild(option);
ar.forEach(function(item, index)
{
let option = document.createElement("option");
option.value = item;
option.text = item;
class.appendChild(option);
});
}).getClass(queue);
};
</script>
<h1>Web App Dependent Drop Down</h1>
<?var url = getUrl();?>
<form method = "post" action = "<?= url ?>">
<label style = "font-size: 20px" >Name</label><br>
<input type = "text" name = "name" style = "font-size: 20px" /><br><br>
<label style = "font-size: 20px" >Colors</label><br>
<select name = "color" style = "font-size: 20px" onchange = "GetFruit(this.value)" >
<option value = "" ></option>
<? for(var i = 0; i < colors.length; i++) { ?>
<option value = "<?= colors[i] ?>" ><?= colors[i] ?></option>
<? } ?>
</select><br><br>
<label style = "font-size: 20px" >Fruit</label><br>
<select name = "fruit" id = "fruit" style = "font-size: 20px" >
</select><br><br>
<label style = "font-size: 20px" >Class</label><br>
<select name = "location" id = "location" style = "font-size: 20px" >
<option value = "" selected disabled>Select Class</option>
</select><br><br>
<label style = "font-size: 20px" >Brand</label><br>
<select name = "location" id = "location" style = "font-size: 20px" >
<option value = "" selected disabled>Select Brand</option>
</select><br><br>
<input type = "submit" name = "submitButton" value = "Submit" style = "font-size: 20px" />
<span style = "font-size: 20px" ><?= message ?></span>
</form>
</body>
</html>
Привет, @Tanaike, спасибо за уделенное время. Я еще не включил сценарий для 4-го уровня, так как я все еще пытаюсь понять 3-й уровень, но я также хочу иметь раскрывающийся список 4-го уровня. Что касается моего листа, у меня есть только два листа: «ДАННЫЕ» и «LOV» для раскрывающегося списка, он имеет 4 столбца (цвет, фрукты, класс, бренд). Пример данных строки: Цвет: Красный, Фрукт: Яблоко, Класс: A, Марка: X).
Спасибо за ответ. Из вашего ответа я предложил модифицированный сценарий в качестве ответа. Не могли бы вы подтвердить это? Если я неправильно понял ваш вопрос и это было бесполезно, приношу свои извинения. К сожалению, кажется, что на текущем этапе изображение не может быть загружено. Итак, теперь я не мог загрузить тестовую ситуацию в виде изображения. Прошу прощения за это.
Привет @Tanaike. Приносим извинения за поздний ответ. Удивительно, это загружается намного быстрее. Большое спасибо. Единственная проблема, которую я заметил, - это раскрывающийся список уровня 4, который является брендом, он не меняется динамически в зависимости от выбора класса.
О The only issue I've noticed is the level 4 dropdown which is the brand, it does not dynamically change based on Class selection., когда «Класс 1» изменяется на «Класс 2», значение «Бренд» изменяется. Прошу прощения за это. Чтобы правильно понять это, можете ли вы предоставить образец электронной таблицы, включая предложенный мной сценарий для ее правильной репликации? Этим я хотел бы подтвердить это.





Вы можете использовать следующие GAS и HTML:
Я добавил функцию onlyUnique (из другого поста SO), чтобы отфильтровать похожие записи. Я также добавил другие функции для столбцов Class и Brand. Я также изменил переменную class на classParam, так как class — зарезервированное слово.
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile('DependentSelect');
var colors = getColors();
var fruits = getFruits();
var classParams = getClasses();
var brands = getBrands();
htmlOutput.message = '';
htmlOutput.colors = colors;
htmlOutput.fruits = fruits;
htmlOutput.classParams = classParams;
htmlOutput.brands = brands;
return htmlOutput.evaluate();
}
function doPost(e) {
Logger.log(JSON.stringify(e));
var name = e.parameters.name.toString();
var color = e.parameters.color.toString();
var fruit = e.parameters.fruit.toString();
var classParam = e.parameters.classParam.toString();
var brand = e.parameters.brand.toString();
AddRecord(name, color, fruit, classParam, brand);
var htmlOutput = HtmlService.createTemplateFromFile('DependentSelect');
var colors = getColors();
var fruits = getFruits();
var classParams = getClasses();
var brands = getBrands();
htmlOutput.message = 'Record Added';
htmlOutput.colors = colors;
htmlOutput.fruits = fruits;
htmlOutput.classParams = classParams;
htmlOutput.brands = brands;
return htmlOutput.evaluate();
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
function getColors() {
var ss= SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for(var i = 2; i <= getLastRow; i++)
{
if (return_array.indexOf(lovSheet.getRange(i, 1).getValue()) === -1) {
return_array.push(lovSheet.getRange(i, 1).getValue());
}
}
return return_array.filter(onlyUnique);
}
function getFruits(color) {
var ss= SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for(var i = 2; i <= getLastRow; i++)
{
if (lovSheet.getRange(i, 1).getValue() === color) {
return_array.push(lovSheet.getRange(i, 2).getValue());
}
}
return return_array.filter(onlyUnique);
}
function getClasses(color, fruit) {
var ss= SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for(var i = 2; i <= getLastRow; i++)
{
if ((lovSheet.getRange(i, 1).getValue() === color) && (lovSheet.getRange(i, 2).getValue() === fruit)) {
return_array.push(lovSheet.getRange(i, 3).getValue());
}
}
return return_array.filter(onlyUnique);
}
function getBrands(color, fruit, classParam) {
var ss= SpreadsheetApp.getActiveSpreadsheet();
var lovSheet = ss.getSheetByName("LOV");
var getLastRow = lovSheet.getLastRow();
var return_array = [];
for(var i = 2; i <= getLastRow; i++)
{
if ((lovSheet.getRange(i, 1).getValue() === color) && (lovSheet.getRange(i, 2).getValue() === fruit) && (lovSheet.getRange(i, 3).getValue() === classParam)) {
return_array.push(lovSheet.getRange(i, 4).getValue());
}
}
return return_array.filter(onlyUnique);
}
function AddRecord(name, color, fruit, classParam, brand) {
var url = ""; //INSERT SPREADSHEET URL HERE <---------
var ss = SpreadsheetApp.openByUrl(url);
var dataSheet = ss.getSheetByName("DATA");
dataSheet.appendRow([name, color, fruit, classParam, brand, new Date()]);
}
function getUrl() {
var url = ScriptApp.getService().getUrl();
return url;
}
<!DOCTYPE html>
<html>
<head>
<base target = "_top">
<script>
function GetFruit(color)
{
google.script.run.withSuccessHandler(function(ar)
{
console.info(ar);
fruit.length = 0;
let option = document.createElement("option");
option.value = "";
option.text = "";
fruit.appendChild(option);
ar.forEach(function(item, index)
{
let option = document.createElement("option");
option.value = item;
option.text = item;
fruit.appendChild(option);
});
}).getFruits(color);
};
function GetClass(color, fruit)
{
google.script.run.withSuccessHandler(function(ar)
{
console.info(ar);
classParam.length = 0;
let option = document.createElement("option");
option.value = "";
option.text = "";
classParam.appendChild(option);
ar.forEach(function(item, index)
{
let option = document.createElement("option");
option.value = item;
option.text = item;
classParam.appendChild(option);
});
}).getClasses(color, fruit);
};
function GetBrand(color, fruit, classParam)
{
google.script.run.withSuccessHandler(function(ar)
{
console.info(ar);
brand.length = 0;
let option = document.createElement("option");
option.value = "";
option.text = "";
brand.appendChild(option);
ar.forEach(function(item, index)
{
let option = document.createElement("option");
option.value = item;
option.text = item;
brand.appendChild(option);
});
}).getBrands(color, fruit, classParam);
};
</script>
</head>
<body>
<h1>Web App Dependent Drop Down</h1>
<?var url = getUrl();?>
<form method = "post" action = "<?= url ?>" >
<!-- name -->
<label style = "font-size: 20px" >Name</label><br>
<input type = "text" name = "name" style = "font-size: 20px" /><br><br>
<!-- color -->
<label style = "font-size: 20px" >Colors</label><br>
<select name = "color" id = "color" style = "font-size: 20px" onchange = "GetFruit(this.value)" >
<option value = "" ></option>
<? for(var i = 0; i < colors.length; i++) { ?>
<option value = "<?= colors[i] ?>" ><?= colors[i] ?></option>
<? } ?>
</select><br><br>
<!-- fruit -->
<label style = "font-size: 20px" >Fruits</label><br>
<select name = "fruit" id = "fruit" style = "font-size: 20px" onchange = "GetClass(color.value, this.value)" >
<option value = "" ></option>
<? for(var i = 0; i < fruits.length; i++) { ?>
<option value = "<?= fruits[i] ?>" ><?= fruits[i] ?></option>
<? } ?>
</select><br><br>
<!-- class -->
<label style = "font-size: 20px" >Classes</label><br>
<select name = "classParam" id = "classParam" style = "font-size: 20px" onchange = "GetBrand(color.value, fruit.value, this.value)" >
<option value = "" ></option>
<? for(var i = 0; i < classParams.length; i++) { ?>
<option value = "<?= classParams[i] ?>" ><?= classParams[i] ?></option>
<? } ?>
</select><br><br>
<!-- brand -->
<label style = "font-size: 20px" >Brand</label><br>
<select name = "brand" id = "brand" style = "font-size: 20px" >
</select><br><br>
<input type = "submit" name = "submitButton" value = "Submit" style = "font-size: 20px" />
<span style = "font-size: 20px" ><?= message ?></span>
</form>
</body>
</html>
Не стесняйтесь спрашивать, есть ли у вас какие-либо вопросы относительно моего ответа.
Ух! Спасибо, Патрик! Ценю тебя мужик! Я только что заметил, что бренд не меняется динамически в зависимости от выбора классов.
Спасибо, что указали на ошибку. По-видимому, фильтрация для каждого столбца зависит только от первого столбца слева от него. я посмотрю на это
Обновлены как GAS, так и HTML. Я добавил дополнительные коды в условные операторы, а также добавил входные переменные в другие функции. Пожалуйста, протестируйте его на своей стороне.
Привет, ПатрикдС. Приносим извинения за задержку ответа. Ты удивительный!! Это сработало отлично. Благодарим вас за помощь.
Рад слышать. Если мой ответ помог вам, пожалуйста, не стесняйтесь принять мой ответ или проголосовать за него, чтобы другие пользователи (которые могут иметь те же проблемы, что и вы) знали, что они могут использовать этот метод, который я предоставил.
Я считаю, что ваша цель заключается в следующем.
Когда циклический процесс используется с использованием HTML-шаблона, стоимость процесса становится высокой. Реф
Когда используется google.script.run, стоимость процесса становится высокой.
google.script.run используется для отправки значений на сторону скрипта Google Apps вместо отправки формы.google.script.run.Создание параметров в теге select выполняется в Javascript с использованием первых загруженных значений.
На стороне скрипта Google Apps getValue() используется в цикле. В этом случае стоимость процесса становится высокой. Реф
Когда эти моменты будут отражены в вашем сценарии показа, как насчет следующей модификации?
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("LOV");
var [, ...values] = sheet.getDataRange().getDisplayValues();
var htmlOutput = HtmlService.createTemplateFromFile('DependentSelect');
htmlOutput.values = JSON.stringify(values);
htmlOutput.message = '';
return htmlOutput.evaluate();
}
function addRecord({ name, color, fruit, clas, brand }) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DATA");
sheet.appendRow([name, color, fruit, clas, brand, new Date()]);
}
<h1>Web App Dependent Drop Down</h1>
<form>
<label style = "font-size: 20px" >Name</label><br>
<input type = "text" name = "name" style = "font-size: 20px" /><br><br>
<label style = "font-size: 20px" >Colors</label><br>
<select id = "colors" name = "color" style = "font-size: 20px" onchange = "setOptions('fruit', getValues(this.value, 0))" ></select><br><br>
<label style = "font-size: 20px" >Fruit</label><br>
<select name = "fruit" id = "fruit" style = "font-size: 20px" onchange = "setOptions('clas', getValues(this.value, 1))" ></select><br><br>
<label style = "font-size: 20px" >Class</label><br>
<select name = "clas" id = "clas" style = "font-size: 20px" onchange = "setOptions('brand', getValues(this.value, 2))" ></select><br><br>
<label style = "font-size: 20px" >Brand</label><br>
<select name = "brand" id = "brand" style = "font-size: 20px" ></select><br><br>
<input type = "button" name = "submitButton" value = "Submit" style = "font-size: 20px" onclick = "sample(this.parentNode)" >
<span style = "font-size: 20px" ><?= message ?></span>
</form>
<script>
const values = JSON.parse(<?= values ?>);
function setOptions(id, v) {
const s = document.getElementById(id);
s.innerHTML = "";
v.forEach(a => {
const option = document.createElement("option");
option.value = a;
option.innerHTML = a;
s.appendChild(option);
});
}
function getValues(e, i) {
return ["", ...new Set(values.reduce((ar, r) => (r[i] == e && ar.push(r[i + 1]), ar), []))];
}
window.onload = function() {
setOptions("colors", ["", ...new Set(values.map(([a]) => a))]);
}
function sample(e) {
google.script.run.addRecord(e);
}
</script>
Когда вы изменили скрипт Google Apps, измените развертывание как новую версию. При этом измененный скрипт отражается в веб-приложениях. Пожалуйста, будьте осторожны с этим.
Подробнее об этом можно узнать в отчете «Повторное развертывание веб-приложений без изменения URL-адреса веб-приложений для новой IDE».
! Приносим извинения за поздний ответ. Удивительно, это загружается намного быстрее. Большое спасибо. Единственная проблема, которую я заметил, - это раскрывающийся список уровня 4, который является брендом, он не меняется динамически в зависимости от выбора класса.
@coders_key Спасибо за ответ. Насчет The only issue I've noticed is the level 4 dropdown which is the brand, it does not dynamically change based on Class selection., когда я тестировал предложенный мной сценарий, я не смог воспроизвести вашу ситуацию. Прошу прощения за это. Чтобы правильно понять вашу текущую проблему it does not dynamically change based on Class selection, можете ли вы предоставить образец электронной таблицы для ее правильного воспроизведения? Этим я хотел бы подтвердить это.
Привет, например. У меня есть этот набор данных. Когда я попытался отфильтровать пример. Красное->Яблоко->Класс 1> в раскрывающихся списках брендов показаны бренд 1, бренд X и бренд 3. Вместо просто брендов 1 и X. Красное яблоко, класс 1, бренд 1, красное яблоко, класс 1, бренд X, желтый банан, класс 1, бренд 3.
@coders_key Спасибо за ответ. К сожалению, я не смог понять ваш ответ. Могу я спросить вас о деталях вашего ответа?
Я обновил снимок экрана в своем исходном сообщении.
@coders_key Спасибо за ответ. Когда я увидел ваше обновленное изображение, я подумал, что «Класс 1» не является «Брендом 1, Брендом 3, Брендом X» из предоставленных вами образцов данных. Я думаю, что когда это «Класс 1», это «Бренд 1, Бренд 2, Бренд X». Поэтому я не могу понять вашу текущую проблему. Прошу прощения за это. Могу я спросить вас о деталях вашей текущей проблемы и вашей цели? Этим я хотел бы подтвердить это. Кстати, когда «Класс 1» меняется на «Класс 2», значение «Бренд» меняется. Как насчет этого?
@coders_key О! Я заметил, что предоставленные вами данные в вашем ответе были изменены только сейчас. Прошу прощения за это. Но когда «Класс 1» меняется на «Класс 2», значение «Марка» изменяется. Прошу прощения за это. Чтобы правильно понять это, можете ли вы предоставить образец электронной таблицы, включая предложенный мной сценарий для ее правильной репликации? Этим я хотел бы подтвердить это.
В своем вопросе вы говорите
I've been trying to add 3rd and 4th level dependent dropdown. И в вашем текущем сценарии кажется, что ваши текущие проблемы — этоI'm trying to add a 3rd level, but it doesn't seem to work.иI'm not sure if there's a fastest way to load the dropdown from Google sheets. И, когда я увидел ваш сценарий показа, на вашей стороне HTML, кажется, чтоgetClassне вызывается. И, я не могу найти скрипт для 4-го уровня. Кроме того, я не могу представить вашу электронную таблицу. Прошу прощения за это. Могу я уточнить детали вашего вопроса?