Заполнение каскадных выпадающих списков в JSP/Servlet

Предположим, у меня есть три раскрывающихся списка с именами dd1, dd2 и dd3. Значение каждого выпадающего списка исходит из базы данных. Значение dd3 зависит от значения dd2, а значение dd2 зависит от значения dd1. Может ли кто-нибудь сказать мне, как мне вызвать сервлет для этой проблемы?


person deven    schedule 15.02.2010    source источник


Ответы (4)


Есть в основном три способа добиться этого:

  1. Отправьте форму сервлету во время события onchange 1-го раскрывающегося списка (для этого вы можете использовать Javascript), позвольте сервлету получить выбранный элемент 1-го раскрывающегося списка в качестве параметра запроса, пусть он получит связанные значения 2-го раскрывающегося списка из базы данных как Map<String, String>, пусть он хранит их в области запроса. Наконец, позвольте JSP/JSTL отображать значения во втором раскрывающемся списке. Вы можете использовать JSTL (просто поместите jstl-1.2.jar в /WEB-INF/lib) c:forEach для этого. Вы можете предварительно заполнить 1-й список в методе doGet() метода Servlet, связанного со страницей JSP.

     <select name="dd1" onchange="submit()">
         <c:forEach items="${dd1options}" var="option">
             <option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
         </c:forEach>
     </select>
     <select name="dd2" onchange="submit()">
         <c:if test="${empty dd2options}">
             <option>Please select parent</option>
         </c:if>
         <c:forEach items="${dd2options}" var="option">
             <option value="${option.key}" ${param.dd2 == option.key ? 'selected' : ''}>${option.value}</option>
         </c:forEach>
     </select>
     <select name="dd3">
         <c:if test="${empty dd3options}">
             <option>Please select parent</option>
         </c:if>
         <c:forEach items="${dd3options}" var="option">
             <option value="${option.key}" ${param.dd3 == option.key ? 'selected' : ''}>${option.value}</option>
         </c:forEach>
     </select>
    

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

  2. Распечатайте все возможные значения 2-го и 3-го раскрывающегося списка в виде объекта Javascript и используйте функцию Javascript для заполнения 2-го раскрывающегося списка на основе выбранного элемента 1-го раскрывающегося списка во время события onchange 1-го раскрывающегося списка. Здесь не требуется отправка формы и цикл сервера.

     <script>
         var dd2options = ${dd2optionsAsJSObject};
         var dd3options = ${dd3optionsAsJSObject};
         function dd1change(dd1) {
             // Fill dd2 options based on selected dd1 value.
             var selected = dd1.options[dd1.selectedIndex].value;
             ...
         }
         function dd2change(dd2) {
             // Fill dd3 options based on selected dd2 value.
             var selected = dd2.options[dd2.selectedIndex].value;
             ...
         }
     </script>
    
     <select name="dd1" onchange="dd1change(this)">
         <c:forEach items="${dd1options}" var="option">
             <option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
         </c:forEach>
     </select>
     <select name="dd2" onchange="dd2change(this)">
         <option>Please select parent</option>
     </select>
     <select name="dd3">
         <option>Please select parent</option>
     </select>
    

    Однако есть одно предостережение: это может стать излишне длинным и дорогим, если у вас много элементов. Представьте, что у вас есть 3 шага из каждых 100 возможных элементов, это будет означать 100 * 100 * 100 = 1 000 000 элементов в объектах JS. Длина HTML-страницы превысит 1 МБ.

  3. Используйте XMLHttpRequest в Javascript для запуска асинхронного запроса к сервлету во время события onchange 1-го раскрывающегося списка, позвольте сервлету получить выбранный элемент 1-го раскрывающегося списка в качестве параметра запроса, позвольте ему получить связанные значения 2-го раскрывающегося списка из базу данных, верните ее обратно в виде строки XML или JSON . Наконец, позвольте Javascript отображать значения во втором раскрывающемся списке дерева HTML DOM (способ Ajax, как предлагалось ранее). Лучше всего для этого использовать jQuery.

     <%@ page pageEncoding="UTF-8" %>
     <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
     <!DOCTYPE html>
     <html lang="en">
         <head>
             <title>SO question 2263996</title>
             <script src="http://code.jquery.com/jquery-latest.min.js"></script>
             <script>
                 $(document).ready(function() {
                     $('#dd1').change(function() { fillOptions('dd2', this); });
                     $('#dd2').change(function() { fillOptions('dd3', this); });
                 });
                 function fillOptions(ddId, callingElement) {
                     var dd = $('#' + ddId);
                     $.getJSON('json/options?dd=' + ddId + '&val=' + $(callingElement).val(), function(opts) {
                         $('>option', dd).remove(); // Clean old options first.
                         if (opts) {
                             $.each(opts, function(key, value) {
                                 dd.append($('<option/>').val(key).text(value));
                             });
                         } else {
                             dd.append($('<option/>').text("Please select parent"));
                         }
                     });
                 }
             </script>
         </head>
         <body>
             <form>
                 <select id="dd1" name="dd1">
                     <c:forEach items="${dd1}" var="option">
                         <option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
                     </c:forEach>
                 </select>
                 <select id="dd2" name="dd2">
                     <option>Please select parent</option>
                 </select>
                 <select id="dd3" name="dd3">
                     <option>Please select parent</option>
                 </select>
             </form>
         </body>
     </html>
    

    ..где Servlet позади /json/options может выглядеть так:

     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         String dd = request.getParameter("dd"); // ID of child DD to fill options for.
         String val = request.getParameter("val"); // Value of parent DD to find associated child DD options for.
         Map<String, String> options = optionDAO.find(dd, val);
         String json = new Gson().toJson(options);
         response.setContentType("application/json");
         response.setCharacterEncoding("UTF-8");
         response.getWriter().write(json);
     }
    

    Здесь Gson – это Google Gson, который упрощает преобразование полноценных объектов Java в JSON и наоборот. . См. также Как использовать сервлеты и Ajax?

person BalusC    schedule 15.02.2010
comment
Отличный код BalusC. Если вы не возражаете, я хочу задать пару вопросов по вашему последнему методу. В вашей функции fillOption() строка $.getJSON выглядит так, как будто вы отправляете GET-запрос сервлету, но где вы указываете URL-адрес сервлета. Кроме того, результат, отправленный сервлетом, сохраняется в «opts». Итак, «opts» похож на объект JSON? Не могли бы вы объяснить эту строку: $('›option', dd).remove(); еще немного? - person Thang Pham; 28.02.2010
comment
@Harry Pham: $.getJSON описано здесь: api.jquery.com/jQuery.getJSON URL просто /json/options. Также смотрите текст (а не только код). Вы можете просто выбрать любой URL-адрес, который хотите. opts действительно является строкой JSON, возвращаемой сервлетом. Также см. ссылку JSON, чтобы узнать больше об этом. Если вы хорошо знаете Javabeans, то JSON должен выглядеть достаточно знакомо. $('>option', dd).remove() удаляет все предыдущие параметры из раскрывающегося списка, иначе он будет только добавляться, добавляться и т. Д. Кстати, если вам нравятся ответы, просто проголосуйте за них. Я вижу, что вы почти никогда не голосовали. - person BalusC; 28.02.2010
comment
Я вижу это сейчас, первый параметр getJSON — это URL-адрес. Я полный идиот. Насчет голосования, извините. Я даже не осознавал этого. Я вернусь к своему старому посту и обновлю голосование. Большое Вам спасибо. Если у меня возникнут дополнительные вопросы, я вернусь и спрошу вас. - person Thang Pham; 28.02.2010
comment
Поэтому я успешно предварительно заполняю первое поле со списком некоторыми значениями из базы данных. Однако происходит одна странная вещь: когда я выбираю одно из значений из dd1, $('#dd1').change(fillOptions('dd2')); больше никогда не вызывался. Внутри fillOptions я помещаю окно предупреждения. Я вижу окно предупреждения только при первой загрузке страницы, но оно не появляется, когда я выбираю что-либо в первом поле со списком. Есть идеи, почему? - person Thang Pham; 02.03.2010
comment
@Harry Pham: Извините, ошибка в коде :) Было написано из головы, на самом деле не проверял. Теперь это исправлено. - person BalusC; 02.03.2010
comment
@BalusC: ваш пример JQuery не будет работать, так как this внутри функции fillOptions будет указывать на объект Window. это будет указывать только на HTMLSelectElement внутри функции, которую вы передали функции .change. Мне пришлось передать это в fillOptions (fillOptions('dd2', this)) для получения значения с другой стороны (функция fillOptions(ddId, callElement), а затем $(callingElement).val()). - person AndrewBourgeois; 14.10.2012
comment
@AndrewBourgeois, ты гений! Действительно, с этой поправкой, которую вы сделали, она работает. Я внес поправку в пост. Ожидание одобрения.. - person Christian Vielma; 16.01.2013
comment
@BalusC Спасибо за подробный ответ. Это оказало огромную помощь! - person neo108; 03.06.2015

Судя по вашему вопросу, вы действительно не используете веб-фреймворк, а используете сервлеты для рендеринга html.

Я буду мил и скажу, что вы отстали от времени примерно на десятилетие :), люди используют JSP (и веб-фреймворк, такой как struts) для такого рода вещей. Однако, сказав это, вот:

  1. Создайте скрытое поле в своей форме и установите значение «1», «2» или «3» в зависимости от того, какой выпадающий список должен быть заполнен;
  2. В своем сервлете зафиксируйте это значение (request.getParamter()) и используйте его в операторе case/if/else для возврата соответствующих выпадающих значений.

Я повторю еще раз, просто используйте для этого веб-фреймворк или, по крайней мере, старый добрый jsp.

person Ryan Fernandes    schedule 15.02.2010

Для этого вам может понадобиться несколько сервлетов.

Сервлет 1: загрузить значения для первого раскрывающегося списка из базы данных. На странице JSP создайте раскрывающийся список. Когда пользователь выбирает значение, отправьте сервлету два.

Сервлет 2: получить значение из первого списка и выполнить поиск в базе данных значений второго списка. Составьте второй список. Когда пользователь выбирает второе значение, отправьте его на сервлет 3.

Сервлет 3: получить значение, выбранное во втором раскрывающемся списке, и выполнить поиск в базе данных, чтобы получить значения для последнего раскрывающегося списка.

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


     <form action="servlet2.do">
          <select name="dd1" onchange="Your JavaScript Here">
               <option>....
          </select>
     </form>

Вы можете написать JavaScript, который отправляет форму в событии onchange. Опять же, если вы используете существующую библиотеку, такую ​​​​как jQuery, это будет в 10 раз проще.

person Vincent Ramdhanie    schedule 15.02.2010
comment
У меня такая же идея, друг, и спасибо за сотрудничество, но я не знаю, как я могу это назвать, пока dd1 теряет фокус - person deven; 15.02.2010
comment
Вы можете использовать немного javascript для отправки значения. Я добавлю немного. - person Vincent Ramdhanie; 15.02.2010

Это было потрясающе простое решение. Мне нравится, насколько мал код JQuery, и я очень ценю ссылку на API GSON. Все примеры сделали это простой реализацией.

Была одна проблема при создании URL-адреса сервера JSON со ссылкой на родительский SELECT (например, $(this).val()) [необходимо указать атрибут :selected]. Я немного изменил сценарий, чтобы включить предлагаемые обновления. Спасибо за исходный код.

<script>
$(document).ready(function() 
{
    $('#dd1').change(function() { fillOptions('dd1', 'dd2'); });
    $('#dd2').change(function() { fillOptions('dd2', 'dd3'); });
});

function fillOptions(parentId, ddId) 
{
    var dd = $('#' + ddId);
    var jsonURL = 'json/options?dd=' + ddId + '&val=' + $('#' + parentId + ' :selected').val();
    $.getJSON(jsonURL, function(opts) 
    {
        $('>option', dd).remove(); // Clean old options first.
        if (opts) 
        {
            $.each(opts, function(key, value) 
            {
                dd.append($('<option/>').val(key).text(value));
            });
        } 
        else 
        {
            dd.append($('<option/>').text("Please select parent"));
        }
    });
}
</script>
person StephenB    schedule 24.10.2010