Заполнение каскадных выпадающих списков в JSP / Servlet - PullRequest
39 голосов
/ 15 февраля 2010

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

Ответы [ 4 ]

48 голосов
/ 15 февраля 2010

Для этого есть три основных способа:

  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?

4 голосов
/ 15 февраля 2010

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

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

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

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

4 голосов
/ 15 февраля 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 раз проще.

3 голосов
/ 25 октября 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>
...