Populating cascading dropdown lists in JSP/Servlet
Asked Answered
T

4

41

Suppose I am having three dropdownlist controls named dd1, dd2 and dd3. The value of each dropdownlist comes from database. dd3's value depends upon value of dd2 and dd2's value depends on value of dd1. Can anyone tell me how do I call servlet for this problem?

Terracotta answered 15/2, 2010 at 4:1 Comment(0)
J
49

There are basically three ways to achieve this:

  1. Submit form to a servlet during the onchange event of the 1st dropdown (you can use Javascript for this), let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database as a Map<String, String>, let it store them in the request scope. Finally let JSP/JSTL display the values in the 2nd dropdown. You can use JSTL (just drop jstl-1.2.jar in /WEB-INF/lib) c:forEach tag for this. You can prepopulate the 1st list in the doGet() method of the Servlet associated with the JSP page.

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

    Once caveat is however that this will submit the entire form and cause a "flash of content" which may be bad for User Experience. You'll also need to retain the other fields in the same form based on the request parameters. You'll also need to determine in the servlet whether the request is to update a dropdown (child dropdown value is null) or to submit the actual form.

  2. Print all possible values of the 2nd and 3rd dropdown out as a Javascript object and make use of a Javascript function to fill the 2nd dropdown based on the selected item of the 1st dropdown during the onchange event of the 1st dropdown. No form submit and no server cycle is needed here.

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

    One caveat is however that this may become unnecessarily lengthy and expensive when you have a lot of items. Imagine that you have 3 steps of each 100 possible items, that would mean 100 * 100 * 100 = 1,000,000 items in JS objects. The HTML page would grow over 1MB in length.

  3. Make use of XMLHttpRequest in Javascript to fire an asynchronous request to a servlet during the onchange event of the 1st dropdown, let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database, return it back as XML or JSON string. Finally let Javascript display the values in the 2nd dropdown through the HTML DOM tree (the Ajax way, as suggested before). The best way for this would be using 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>
    

    ..where the Servlet behind /json/options can look like this:

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

    Here, Gson is Google Gson which eases converting fullworthy Java objects to JSON and vice versa. See also How to use Servlets and Ajax?

Juncture answered 15/2, 2010 at 15:55 Comment(8)
Awesome code BalusC. If u dont mind, I want to ask a couple questions on your last method. In your function fillOption(), the line $.getJSON, it look like you send a GET request to the servlet, but where do you specify the url of the servlet. In addition, does the result that send back from the servlet get store in 'opts'. So is 'opts' like and JSON object? Can u explain this line: $('>option', dd).remove(); a bit more?Cassimere
@Harry Pham: $.getJSON is documented here: api.jquery.com/jQuery.getJSON The URL is just /json/options. Also see the text (don't only look at code). You can just choose whatever URL you want. The opts is indeed JSON string as returned by servlet. Also see the JSON link to learn more about it. If you know Javabeans good, then JSON should look familiar enough. The $('>option', dd).remove() removes all previous options from the dropdown, otherwise it would only be appended, appended, etc.. By the way, if you like answers, just upvote it. I see that you almost never voted.Juncture
I see it now, the first parameter of getJSON is the url. I am a complete idiot. About the vote thing, so sorry. I did not even realize that. I will look back at my old post and update the vote. Thank you very much. If I have more question, I will come back and ask u.Cassimere
So I successfully pre-populate the first combo box with some values from the db. However 1 weird thing happpen: When I select on one of the value from dd1, $('#dd1').change(fillOptions('dd2')); never got invoked again. Inside fillOptions, I put an alert box. I only see the alert box, when I first load the page, but it wont pop out when I select anything in the first combo box. Any idea why?Cassimere
@Harry Pham: Sorry, code error :) Was written out of head, didn't really test it. It's now fixed.Juncture
@BalusC: Your JQuery example won't work, as your "this" inside the fillOptions function will point to the Window object. "this" will only point to an HTMLSelectElement inside the function you passed to the .change function. I had to pass "this" to the fillOptions ("fillOptions('dd2', this)") in order to retrieve the value at the other side ("function fillOptions(ddId, callingElement)" and then "$(callingElement).val()").Jessalin
@Jessalin man you're genius! Indeed with this correction you made, it works. I made the correction to the post. Waiting to be approved..Sibella
@Juncture Thanks for the elaborate answer. Its been of immense help!Southwards
O
4

You may need multiple servlets for this.

Servlet 1: Load the values for the first drop down list from the database. On the JSP page construct the drop down list. On the user selecting a value submit to servlet two.

Servlet 2: retrieve the value from the first list and perform your database search for the values of the second list. Construct the second list. When the user selects the second value submit it to servlet 3.

Servlet 3: retrieve the value selected in the second drop down and perform the database search to get values for the last drop down.

You may want to consider AJAX to make the populating of the lists appear seamless to the users. jQuery has some very nice plugins for making this quite easy if you are willing to do that.


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

You can write JavaScript that submits the form in the onchange event. Again, If you use an existing library like jQuery it will be 10 times simpler.

Olympium answered 15/2, 2010 at 4:12 Comment(2)
I am having same idea frend and thanks for cooperation but i dont know how can i call it while dd1 looses its focusTerracotta
You can use a bit a javascript to submit the value. I'll add some.Olympium
N
4

Judging by your question, you're really not using a web framework but using servlets to render html.

I'll be nice and say that you're about a decade behind the times :), people use JSPs (and a web framework like struts) for this sort of thing. However, having said that, here goes:

  1. Create a hidden field in your form and set the value to '1', '2' or '3' depending on which drop down is to be populated;
  2. In your servlet, capture this value (request.getParamter()) and use it a 'case'/if/else statement to return the appropriate dropdown values.

I'll say it again, just use a web-framework, or atleast plain old jsp to do this.

Neoprene answered 15/2, 2010 at 4:53 Comment(0)
A
3

That was an awesome simple solution. I like how small the JQuery code is and really appreciate the link to the GSON API. All the examples made this an easy implementation.

Had one issue on building the JSON server URL with the reference to the parent SELECT ( e.g. $(this).val() ) [needed to specify the :selected attribute]. I've modified the script a little to include the suggested updates. Thanks for the initial code.

<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>
Allayne answered 24/10, 2010 at 21:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.