【问题标题】:Populating cascading dropdown lists in JSP/Servlet在 JSP/Servlet 中填充级联下拉列表
【发布时间】:2026-02-18 15:30:01
【问题描述】:

假设我有三个名为dd1dd2dd3 的下拉列表控件。每个下拉列表的值来自数据库。 dd3 的值取决于dd2 的值,dd2 的值取决于dd1 的值。谁能告诉我如何为这个问题调用 servlet?

【问题讨论】:

    标签: jsp servlets drop-down-menu cascading


    【解决方案1】:

    基本上有三种方法可以实现:

    1. 在第一个下拉菜单的onchange事件期间提交表单到一个servlet(可以使用Javascript),让servlet获取第一个下拉菜单的选中项作为请求参数,让它获取第二个下拉菜单的关联值从数据库中作为Map<String, String>,让它将它们存储在请求范围内。最后让 JSP/JSTL 显示第二个下拉列表中的值。您可以为此使用JSTL(只需将jstl-1.2.jar 放入/WEB-INF/libc:forEach 标记。您可以在与 JSP 页面关联的ServletdoGet() 方法中预填充第一个列表。

       <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>
      

      然而,需要注意的是,这将提交整个表单并导致“内容闪现”,这可能对用户体验不利。您还需要根据请求参数以相同的形式保留其他字段。您还需要在 servlet 中确定请求是更新下拉列表(子下拉列表值为空)还是提交实际表单。

    2. 在第一个下拉列表的 onchange 事件期间,将第二个和第三个下拉列表的所有可能值打印为 Javascript 对象,并使用 Javascript 函数根据第一个下拉列表的选定项填充第二个下拉列表。这里不需要提交表单,也不需要服务器周期。

       <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>
      

      但需要注意的是,当您拥有很多物品时,这可能会变得不必要的冗长和昂贵。想象一下,每 100 个可能的项目有 3 个步骤,这意味着 JS 对象中有 100 * 100 * 100 = 1,000,000 个项目。 HTML 页面的长度将超过 1MB。

    3. 利用Javascript中的XMLHttpRequest在第一个下拉菜单的onchange事件期间向servlet发起一个异步请求,让servlet获取第一个下拉菜单的选中项作为请求参数,让它获取第二个下拉菜单的关联值从数据库中下拉,将其作为 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>
      

      ../json/options 后面的 Servlet 可能如下所示:

       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);
       }
      

      这里,GsonGoogle Gson,它可以轻松地将完全有价值的 Java 对象转换为 JSON,反之亦然。另见How to use Servlets and Ajax?

    【讨论】:

    • 很棒的代码 BalusC。如果你不介意,我想就你的最后一种方法问几个问题。在您的函数 fillOption() 中,$.getJSON 行,看起来您向 servlet 发送了一个 GET 请求,但是您在哪里指定 servlet 的 url。此外,从 servlet 发回的结果是否存储在“opts”中。 'opts' 和 JSON 对象一样吗?你能解释一下这一行吗: $('>option', dd).remove();多一点?
    • @Harry Pham:$.getJSON 记录在这里:api.jquery.com/jQuery.getJSON URL 只是/json/options。另请参阅文本(不要只看代码)。您可以选择任何您想要的 URL。 opts 确实是 servlet 返回的 JSON 字符串。另请参阅 JSON 链接以了解更多信息。如果您对 Javabeans 非常了解,那么 JSON 应该看起来已经足够熟悉了。 $('&gt;option', dd).remove() 会从下拉列表中删除所有以前的选项,否则它只会被附加、附加等。顺便说一句,如果您喜欢答案,请点赞。我看到你几乎从未投票过。
    • 现在看到了,getJSON的第一个参数就是url。我是个彻头彻尾的白痴。关于投票的事情,很抱歉。我什至没有意识到这一点。我将回顾我的旧帖子并更新投票。非常感谢。如果我有更多问题,我会回来问你。
    • 所以我成功地用数据库中的一些值预先填充了第一个组合框。然而,发生了一件奇怪的事情:当我从 dd1 中选择一个值时,$('#dd1').change(fillOptions('dd2'));再也没有被调用。在 fillOptions 里面,我放了一个警告框。当我第一次加载页面时,我只看到警告框,但当我在第一个组合框中选择任何内容时,它不会弹出。知道为什么吗?
    • @BalusC:您的 JQuery 示例不起作用,因为您在 fillOptions 函数中的“this”将指向 Window 对象。 “this”将仅指向您传递给 .change 函数的函数内的 HTMLSelectElement。我必须将“this”传递给fillOptions(“fillOptions('dd2',this)”)才能检索另一边的值(“function fillOptions(ddId,callingElement)”,然后是“$(callingElement)。 val()").
    【解决方案2】:

    从您的问题来看,您实际上并没有使用 Web 框架,而是使用 servlet 来呈现 html。

    我会很高兴地说你落后了大约十年 :),人们使用 JSP(和类似 struts 的 Web 框架)来处理这类事情。然而,话虽如此,这里是:

    1. 在表单中创建一个隐藏字段并将值设置为“1”、“2”或“3”,具体取决于要填充的下拉列表;
    2. 在您的 servlet 中,捕获该值 (request.getParamter()) 并将其用作“case”/if/else 语句以返回适当的下拉值。

    我再说一遍,只需使用网络框架,或者至少是普通的旧 jsp 来做到这一点。

    【讨论】:

      【解决方案3】:

      您可能需要多个 servlet。

      Servlet 1:从数据库加载第一个下拉列表的值。在 JSP 页面上构建下拉列表。在用户选择一个值时提交给 servlet 2。

      Servlet 2:从第一个列表中检索值并执行数据库搜索以查找第二个列表的值。构造第二个列表。当用户选择第二个值时,将其提交给 servlet 3。

      Servlet 3:检索在第二个下拉菜单中选择的值并执行数据库搜索以获取最后一个下拉菜单的值。

      您可能需要考虑使用 AJAX 来使列表的填充对用户来说是无缝的。如果你愿意的话,jQuery 有一些非常好的插件可以让这变得非常简单。


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

      您可以编写在 onchange 事件中提交表单的 JavaScript。同样,如果你使用像 jQuery 这样的现有库,它会简单 10 倍。

      【讨论】:

      • 朋友和我有同样的想法,感谢合作,但我不知道在 dd1 失去焦点时如何称呼它
      • 您可以使用一点 javascript 来提交值。我会添加一些。
      【解决方案4】:

      这是一个很棒的简单解决方案。我喜欢 JQuery 代码有多小,并且非常感谢 GSON API 的链接。所有的例子都让这个实现变得很容易。

      在使用对父 SELECT 的引用(例如 $(this).val() )构建 JSON 服务器 URL 时遇到一个问题 [需要指定 :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>
      

      【讨论】:

        最近更新 更多