Динамическое заполнение поля со списком значениями из карты на основе того, что выбрано в другом поле со списком

Хорошо, вот один для гуру Java / JavaScript:

В моем приложении один из контроллеров передает TreeMap своему JSP. На этой карте названия производителей автомобилей указаны в качестве ключей, а объекты Lists of Car - в качестве значений. Эти объекты Car представляют собой простые бобы, содержащие имя автомобиля, идентификатор, год выпуска и т. д. Итак, карта выглядит примерно так (это просто пример, чтобы немного прояснить ситуацию):

Ключ: Porsche
Значение: список, содержащий три объекта "Автомобиль" (например, 911, Carrera, Boxter с их приличными годами производства и идентификаторами)
Ключ: Fiat
Значение: список, содержащий два объекта "Автомобиль" (например, Punto и Uno)
так далее...

Теперь в моем JSP есть два комбинированных списка. Один должен получить список производителей автомобилей (ключи с карты - эту часть я знаю, как это сделать), а другой должен динамическое изменение для отображения названий автомобилей, когда пользователь выбирает определенного производителя из первого поля со списком. Так, например, пользователь выбирает «Porsche» в первом выпадающем списке, а второй сразу отображает «911, Carrera, Boxter» ...

Потратив пару дней на то, чтобы понять, как это сделать, я готов признать поражение. Я пробовал много разных вещей, но каждый раз на пути где-то ударялся о стену. Кто-нибудь может подсказать, как мне подойти к этому? Да, я новичок в JavaScript, если кому-то интересно ...

Обновлено: Я пометил это как вызов кода. Престижность всем, кто решает эту проблему без использования какой-либо инфраструктуры JavaScript (например, JQuery).

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
18 038
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Вы используете Struts?

Для этого вам понадобится уловка JavaScript (или AJAX).

Что вам нужно сделать, так это где-то в вашем коде JavaScript (не говоря уже о том, как вы его генерируете на минуту):

var map = {
   'porsche': [ 'boxter', '911', 'carrera' ],
   'fiat': ['punto', 'uno']
};

По сути, это копия вашей серверной структуры данных, то есть карта с ключом производителя, каждое значение имеет массив типов автомобилей.

Затем, в событии onchange для производителей, вам нужно будет получить массив из карты, определенной выше, а затем создать из него список параметров. (Посетите devguru.com - там много полезной информации о стандартных объектах JavaScript).

Однако, в зависимости от того, насколько велик ваш список автомобилей, лучше всего выбрать AJAX-маршрут.

Вам нужно будет создать новый контроллер, который будет искать список типов автомобилей, указанный производителем, а затем перейти к JSP, который вернет JSON (это не обязательно должен быть JSON, но для меня он работает довольно хорошо).

Затем используйте библиотеку, такую ​​как jQuery, чтобы получить список автомобилей в вашем событии onchange для списка производителей. (jQuery - отличный JavaScript-фреймворк, который нужно знать - он значительно упрощает разработку с помощью JavaScript. Документация очень хороша).

Надеюсь, в этом есть смысл?

Спасибо за ответ, Фил. На самом деле я использую Spring. Хотя ваш ответ хорош, я все же хотел бы услышать другие мнения :-)

Sandman 01.10.2008 13:34

Как насчет того, чтобы использовать прототип? Во-первых, ваше поле выбора категорий:

<SELECT onchange = "changeCategory(this.options[this.selectedIndex].value); return false;">
   <OPTION value = "#categoryID#">#category#</OPTION>
   ...

Затем вы выводите N разных полей выбора, по одному для каждой подкатегории:

<SELECT name = "myFormVar" class = "categorySelect">
...                                        

Ваша функция javascript changeCategory отключает все выборки с помощью класса categorySelect, а затем включает только один для вашего текущего идентификатора категории.

// Hide all category select boxes except the new one
function changeCategory(categoryID) {

   $$("select.categorySelect").each(function (select) {
      select.hide();
      select.disable();
   });

   $(categoryID).show();
   $(categoryID).enable();
}

Когда вы скрываете / отключаете это в прототипе, он не только скрывает его на странице, но и предотвращает публикацию этой переменной FORM. Таким образом, даже если у вас есть N выборок с тем же именем переменной FORM (myFormVar), только активная публикация.

Если я правильно понимаю, каждый раз, когда производитель автомобилей добавляется или удаляется с карты, мне придется добавлять или удалять соответствующий тег выбора в JSP. Поскольку я получаю эту карту из веб-службы поставщика, у меня нет возможности узнать, сколько элементов может иметь карта.

Sandman 02.10.2008 11:20

И я до сих пор не знаю, как узнать названия машин. Имя автомобиля представляет собой String в объекте Car, который хранится в List, который хранится как значение в Map. Уф ... поговорим о сложном ...

Sandman 02.10.2008 11:25

Не так давно я думал о чем-то подобном.

Используя jQuery и надстройку TexoTela, это было не так уж и сложно.

Во-первых, у вас есть структура данных, подобная упомянутой выше карте:

var map = {
   'porsche': [ 'boxter', '911', 'carrera' ],
   'fiat': ['punto', 'uno']
}; 

Ваш HTML должен выглядеть примерно так:

<select size = "4" id = "manufacturers">
</select>
<select size = "4" id = "models">
</select>

Затем вы заполняете первую комбинацию кодом jQuery, например:

$(document).ready(
  function() {
    $("#bronsysteem").change( manufacturerSelected() );
  } );
);

где ManufacturerSelected - обратный вызов, зарегистрированный в событии onChange

function manufacturerSelected() {
  newSelection = $("#manufacturers").selectedValues();
  if (newSelection.length != 1) {
    alert("Expected a selection!");
    return; 
  }
  newSelection = newSelection[0];
  fillModels(newSelection);     
}

function fillModels(manufacterer) {
    var models = map[manufacturer];

    $("models").removeOption(/./); // Empty combo

    for(modelId in models) {
       model = models[modelId];
       $("models").addOption(model,model); // Value, Text
    }
}

Это должно помочь.

Обратите внимание, что там могут быть синтаксические ошибки; Я отредактировал свой код, чтобы отразить ваш вариант использования, и мне пришлось удалить довольно много вышло.

Если это поможет, я был бы признателен за комментарий.

Прежде всего, спасибо за ответ, extraneon. Я воспользуюсь им, если другого выхода не найду. Однако здесь есть две проблемы. Во-первых, я делаю это не для своего личного проекта, а как часть более крупного корпоративного приложения для компании, в которой я сейчас работаю. Из-за этого,

Sandman 04.10.2008 14:19

Возможно, мне не разрешат использовать JQuery. Они немного раздражительны, когда дело доходит до внедрения новых фреймворков в приложение. Но, если я не смогу заставить его работать по-другому, мне просто нужно будет попросить моего начальника убедить бизнесменов, что это единственный способ сделать это. Однако более серьезная проблема - получить карту

Sandman 04.10.2008 14:21

работать в JavaScript. Я уже упоминал, что получаю его из веб-службы, и мой контроллер передает его в JSP, поэтому я не могу просто жестко закодировать его в JavaScript, я должен иметь возможность сопоставить свою карту с объектом JavaScript, чтобы я мог манипулируйте им там. Я все еще работаю над своим собственным решением, поэтому

Sandman 04.10.2008 14:23

если где-нибудь доберусь, выложу решение здесь. Еще раз всем, спасибо за все ответы и извините за длинный комментарий.

Sandman 04.10.2008 14:25

В качестве дополнения к моему предыдущему сообщению; Вы можете поместить тег скрипта в свой JSP, где вы перебираете карту. Пример перебора карт можно найти в Карты в Struts.

Чего вы хотели бы достичь (если вас не волнует отправка формы), я думаю, что-то вроде:

<script>
  var map = {
  <logic:iterate id = "entry" name = "myForm" property = "myMap">
     '<bean:write name = " user" property = "key"/>' : [
     <logic:iterate id = "model" name = "entry" property = "value">
       '<bean:write name = " model" property = "name"/>' ,
     </logic:iterate>
     ] ,
 </logic:iterate>
  };
</script>

У вас все еще есть лишнее "," которое вы могли бы предотвратить, но я думаю, что это должно помочь.

Я просто люблю вызовы.

Никакого jQuery, только простой javascript, протестированный в Safari.

Заранее хочу добавить следующие замечания:

  • Это долго из-за ошибки проверка.
  • Генерируются две части; первый узел скрипта с картой и содержимое производителя ВЫБРАТЬ
  • Работает на моей машине (TM) (Safari / OS X)
  • Нет (css) стилизация применена. У меня дурной вкус в любом случае это бесполезно.

.

<body>
  <script>
  // DYNAMIC
  // Generate in JSP
  // You can put the script tag in the body
  var modelsPerManufacturer = {
    'porsche' : [ 'boxter', '911', 'carrera' ],
    'fiat': [ 'punto', 'uno' ]  
  };
  </script>

  <script>
  // STATIC
  function setSelectOptionsForModels(modelArray) {
    var selectBox = document.myForm.models;

    for (i = selectBox.length - 1; i>= 0; i--) {
    // Bottom-up for less flicker
    selectBox.remove(i);  
    }

    for (i = 0; i< modelArray.length; i++) {
     var text = modelArray[i];
      var opt = new Option(text,text, false, false);
      selectBox.add(opt);
    }  
  }

  function setModels() {
    var index = document.myForm.manufacturer.selectedIndex;
    if (index == -1) {
    return;
    }

    var manufacturerOption = document.myForm.manufacturer.options[index];
    if (!manufacturerOption) {
      // Strange, the form does not have an option with given index.
      return;
    }
    manufacturer = manufacturerOption.value;

    var modelsForManufacturer = modelsPerManufacturer[manufacturer];
    if (!modelsForManufacturer) {
      // This modelsForManufacturer is not in the modelsPerManufacturer map
      return; // or alert
    }   
    setSelectOptionsForModels(modelsForManufacturer);
  }

  function modelSelected() {
    var index = document.myForm.models.selectedIndex;
    if (index == -1) {
      return;
    }
    alert("You selected " + document.myForm.models.options[index].value);
  }
  </script>
  <form name = "myForm">
    <select onchange = "setModels()" id = "manufacturer" size = "5">
      <!-- Options generated by the JSP -->
      <!-- value is index of the modelsPerManufacturer map -->
      <option value = "porsche">Porsche</option>
      <option value = "fiat">Fiat</option>
    </select>

    <select onChange = "modelSelected()" id = "models" size = "5">
      <!-- Filled dynamically by setModels -->
    </select>
  </form>

</body>

Очень мило, extraneon! Однако карта все еще жестко запрограммирована, поэтому проблема все еще существует ... :-) Честно говоря, мне удалось решить проблему, хотя решение не очень хорошее. Часть заполнения выполняется примерно так же, как и вы. Я еще не опубликовал его, потому что у меня много дел, но я опубликую

Sandman 07.10.2008 22:41

это как можно скорее, наверное, послезавтра. Хотя ваше решение не является законченным, я даю вам голос только за усилия, которые вы вложили в него :-) Подсказка: мое решение включает использование скриптлета.

Sandman 07.10.2008 22:45

В ПОРЯДКЕ. Я должен был написать JSP и динамически генерировать modelsPerManufacturer и SELECT производителя. не используя ничего, кроме входной карты. Я обновлю ответ сегодня вечером (сейчас 7 утра).

extraneon 08.10.2008 09:14

Вот рабочий, вырезанный и вставленный ответ в jsp без каких-либо библиотек тегов или каких-либо внешних зависимостей. Карта с моделями жестко запрограммирована, но не должна вызывать никаких проблем.

Я отделил этот ответ от своего предыдущего ответа, поскольку добавленный JSP не улучшает читаемость. И в «реальной жизни» я бы не стал обременять свой JSP всей встроенной логикой, а поместил бы его где-нибудь в классе. Или используйте теги.

Все, что «в первую очередь» нужно для предотвращения "избыточного потока" в сгенерированном коде. Использование foreach не дает вам никаких сведений о количестве элементов, поэтому вы проверяете последнее. Вы также можете пропустить обработку первого элемента и удалить последний "," впоследствии, уменьшив длину компоновщика на 1.

<%@ page language = "java" contentType = "text/html; charset=ISO-8859-1"
    pageEncoding = "ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page import = "java.util.Map"%>
<%@page import = "java.util.TreeMap"%>
<%@page import = "java.util.Arrays"%>
<%@page import = "java.util.Collection"%>
<%@page import = "java.util.List"%>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html; charset=ISO-8859-1">
<title>Challenge</title>
</head>
<body onload = "setModels()">
<% // You would get your map some other way.
    Map<String,List<String>> map = new TreeMap<String,List<String>>();
    map.put("porsche", Arrays.asList(new String[]{"911", "Carrera"}));
    map.put("mercedes", Arrays.asList(new String[]{"foo", "bar"}));
%>

<%! // You may wish to put this in a class
  public String modelsToJavascriptList(Collection<String> items) {
    StringBuilder builder = new StringBuilder();
    builder.append('[');
    boolean first = true;
    for (String item : items) {
        if (!first) {
          builder.append(',');
        } else {
          first = false;
        }
        builder.append('\'').append(item).append('\'');
    }
    builder.append(']');
    return builder.toString();
  }

  public String mfMapToString(Map<String,List<String>> mfmap) {
    StringBuilder builder = new StringBuilder();
    builder.append('{');
    boolean first = true;
    for (String mf : mfmap.keySet()) {
      if (!first) {
          builder.append(',');
      } else {
          first = false;
      }
      builder.append('\'').append(mf).append('\'');
      builder.append(" : ");
      builder.append( modelsToJavascriptList(mfmap.get(mf)) );
    }
    builder.append("};");
    return builder.toString();
  }
%>

<script>
var modelsPerManufacturer =<%= mfMapToString(map) %>
  function setSelectOptionsForModels(modelArray) {
    var selectBox = document.myForm.models;

    for (i = selectBox.length - 1; i>= 0; i--) {
    // Bottom-up for less flicker
    selectBox.remove(i);
    }

    for (i = 0; i< modelArray.length; i++) {
     var text = modelArray[i];
      var opt = new Option(text,text, false, false);
      selectBox.add(opt);
    }
  }

  function setModels() {
    var index = document.myForm.manufacturer.selectedIndex;
    if (index == -1) {
    return;
    }

    var manufacturerOption = document.myForm.manufacturer.options[index];
    if (!manufacturerOption) {
      // Strange, the form does not have an option with given index.
      return;
    }
    manufacturer = manufacturerOption.value;

    var modelsForManufacturer = modelsPerManufacturer[manufacturer];
    if (!modelsForManufacturer) {
      // This modelsForManufacturer is not in the modelsPerManufacturer map
      return; // or alert
    }
    setSelectOptionsForModels(modelsForManufacturer);
  }

  function modelSelected() {
    var index = document.myForm.models.selectedIndex;
    if (index == -1) {
      return;
    }
    alert("You selected " + document.myForm.models.options[index].value);
  }
  </script>
  <form name = "myForm">
    <select onchange = "setModels()" id = "manufacturer" size = "5">
      <% boolean first = true;
         for (String mf : map.keySet()) { %>
          <option value = "<%= mf %>" <%= first ? "SELECTED" : "" %>><%= mf %></option>
      <%   first = false;
         } %>
    </select>

    <select onChange = "modelSelected()" id = "models" size = "5">
      <!-- Filled dynamically by setModels -->
    </select>
  </form>

</body>
</html>
Ответ принят как подходящий

В любом случае, как я уже сказал, мне наконец удалось сделать это самому, так что вот мой ответ ...

Я получаю карту от своего контроллера следующим образом (я использую Spring, не знаю, как это работает с другими фреймворками):

<c:set var = "manufacturersAndModels" scope = "page" value = "${MANUFACTURERS_AND_MODELS_MAP}"/>

Это мои комбо:

<select id = "manufacturersList" name = "manufacturersList" onchange = "populateModelsCombo(this.options[this.selectedIndex].index);" >
                  <c:forEach var = "manufacturersItem" items = "<%= manufacturers%>">
                    <option value='<c:out value = "${manufacturersItem}" />'><c:out value = "${manufacturersItem}" /></option>
                  </c:forEach>
                </select>


select id = "modelsList" name = "modelsList"
                  <c:forEach var = "model" items = "<%= models %>" >
                    <option value='<c:out value = "${model}" />'><c:out value = "${model}" /></option>
                  </c:forEach>
                </select>

Я импортировал следующие классы (некоторые названия, конечно, были изменены):

<%@ page import = "org.mycompany.Car,java.util.Map,java.util.TreeMap,java.util.List,java.util.ArrayList,java.util.Set,java.util.Iterator;" %>

А вот код, который выполняет всю тяжелую работу:

<script type = "text/javascript">
<%  
     Map mansAndModels = new TreeMap();
     mansAndModels = (TreeMap) pageContext.getAttribute("manufacturersAndModels");
     Set manufacturers = mansAndModels.keySet(); //We'll use this one to populate the first combo
     Object[] manufacturersArray = manufacturers.toArray();

     List cars;
     List models = new ArrayList(); //We'll populate the second combo the first time the page is displayed with this list


 //initial second combo population
     cars = (List) mansAndModels.get(manufacturersArray[0]);

     for(Iterator iter = cars.iterator(); iter.hasNext();) {

       Car car = (Car) iter.next();
       models.add(car.getModel());
     }
%>


function populateModelsCombo(key) {
  var modelsArray = new Array();

  //Here goes the tricky part, we populate a two-dimensional javascript array with values from the map
<%                          
     for(int i = 0; i < manufacturersArray.length; i++) {

       cars = (List) mansAndModels.get(manufacturersArray[i]);
       Iterator carsIterator = cars.iterator();           
%>
    singleManufacturerModelsArray = new Array();
<%
    for(int j = 0; carsIterator.hasNext(); j++) {

      Car car = (Car) carsIterator.next();

 %>         
    singleManufacturerModelsArray[<%= j%>] = "<%= car.getModel()%>";
 <%
       }
 %>
  modelsArray[<%= i%>] = singleManufacturerModelsArray;
 <%
     }         
 %>   

  var modelsList = document.getElementById("modelsList");

  //Empty the second combo
  while(modelsList.hasChildNodes()) {
    modelsList.removeChild(modelsList.childNodes[0]);
  }

 //Populate the second combo with new values
  for (i = 0; i < modelsArray[key].length; i++) {

    modelsList.options[i] = new Option(modelsArray[key][i], modelsArray[key][i]);
  }      
}

Другие вопросы по теме