web-dev-qa-db-fra.com

Remplissage des listes déroulantes en cascade dans JSP / Servlet

Supposons que j'ai trois contrôles de liste déroulante nommés dd1, dd2 Et dd3. La valeur de chaque liste déroulante provient de la base de données. La valeur de dd3 Dépend de la valeur de dd2 Et la valeur de dd2 Dépend de la valeur de dd1. Quelqu'un peut-il me dire comment appeler le servlet pour ce problème?

39
deven

Il existe essentiellement trois façons d'y parvenir:

  1. Soumettez le formulaire à un servlet pendant l'événement onchange du 1er menu déroulant (vous pouvez utiliser Javascript pour cela), laissez le servlet obtenir l'élément sélectionné du 1er menu déroulant comme paramètre de demande, laissez-le obtenir les valeurs associées du deuxième menu déroulant à partir de la base de données en tant que Map<String, String>, laissez-le les stocker dans la portée de la demande. Enfin, laissez JSP/JSTL afficher les valeurs dans la 2ème liste déroulante. Vous pouvez utiliser JSTL (déposez simplement jstl-1.2.jar dans /WEB-INF/lib) c:forEach tag pour cela. Vous pouvez préremplir la 1ère liste dans la méthode doGet() de la Servlet associée à la page 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>
    

    Une fois cependant, cela soumettra le formulaire entier et provoquera un "flash de contenu" qui peut être mauvais pour l'expérience utilisateur. Vous devrez également conserver les autres champs dans le même formulaire en fonction des paramètres de la demande. Vous devrez également déterminer dans la servlet si la demande consiste à mettre à jour une liste déroulante (la valeur de la liste déroulante enfant est nulle) ou à soumettre le formulaire réel.

  2. Imprimez toutes les valeurs possibles des 2e et 3e listes déroulantes en tant qu'objets Javascript et utilisez une fonction Javascript pour remplir la 2e liste déroulante en fonction de l'élément sélectionné de la 1ère liste déroulante lors de l'événement onchange de la 1ère liste déroulante. Aucun formulaire à soumettre et aucun cycle de serveur n'est nécessaire ici.

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

    Une mise en garde est cependant que cela peut devenir inutilement long et coûteux lorsque vous avez beaucoup d'articles. Imaginez que vous ayez 3 étapes pour 100 éléments possibles, ce qui signifie 100 * 100 * 100 = 1 000 000 éléments dans les objets JS. La page HTML aurait une taille supérieure à 1 Mo.

  3. Utilisez XMLHttpRequest en Javascript pour lancer une demande asynchrone vers un servlet pendant l'événement onchange du 1er menu déroulant, laissez le servlet obtenir l'élément sélectionné du 1er menu déroulant en tant que paramètre de demande, laissez-le obtenir les valeurs associées du 2e menu déroulant à partir du base de données, renvoyez-la au format XML ou JSON chaîne. Enfin, laissez Javascript afficher les valeurs dans la 2ème liste déroulante à travers l'arborescence DOM HTML (à la manière Ajax, comme suggéré précédemment). La meilleure façon serait d'utiliser 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>
    

    ..où le Servlet derrière /json/options peut ressembler à ceci:

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

    Ici, Gson est Google Gson ce qui facilite la conversion des objets Java objets Java en JSON et vice versa. Voir aussi Comment utiliser les servlets et Ajax?

48
BalusC

À en juger par votre question, vous n'utilisez pas vraiment un cadre Web, mais des servlets pour rendre le code HTML.

Je serai gentil et je dirai que vous avez environ une décennie de retard :), les gens utilisent des JSP (et un framework web comme struts) pour ce genre de choses. Cependant, cela dit, voici:

  1. Créez un champ masqué dans votre formulaire et définissez la valeur sur "1", "2" ou "3" en fonction de la liste déroulante à remplir;
  2. Dans votre servlet, capturez cette valeur (request.getParamter ()) et utilisez-la une instruction 'case'/if/else pour renvoyer les valeurs de liste déroulante appropriées.

Je le répète, utilisez simplement un framework web, ou tout au moins un vieux jsp pour le faire.

4
Ryan Fernandes

Vous aurez peut-être besoin de plusieurs servlets pour cela.

Servlet 1: chargez les valeurs de la première liste déroulante à partir de la base de données. Sur la page JSP, créez la liste déroulante. Sur l'utilisateur sélectionnant une valeur, soumettez à la servlet deux.

Servlet 2: récupérez la valeur de la première liste et effectuez votre recherche dans la base de données pour les valeurs de la deuxième liste. Construisez la deuxième liste. Lorsque l'utilisateur sélectionne la deuxième valeur, soumettez-la à la servlet 3.

Servlet 3: récupérez la valeur sélectionnée dans la deuxième liste déroulante et effectuez la recherche dans la base de données pour obtenir les valeurs de la dernière liste déroulante.

Vous voudrez peut-être envisager AJAX pour rendre le remplissage des listes transparent pour les utilisateurs. JQuery a quelques plugins très agréables pour rendre cela assez facile si vous êtes disposé à le faire.


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

Vous pouvez écrire du JavaScript qui soumet le formulaire dans l'événement onchange. Encore une fois, si vous utilisez une bibliothèque existante comme jQuery, ce sera 10 fois plus simple.

4
Vincent Ramdhanie

C'était une solution simple impressionnante. J'aime la petite taille du code JQuery et apprécie vraiment le lien vers l'API GSON. Tous les exemples ont rendu cette implémentation facile.

Nous avons rencontré un problème lors de la création de l'URL du serveur JSON avec la référence au SELECT parent (par exemple $(this).val()) [nécessaire pour spécifier le :selected attribut]. J'ai un peu modifié le script pour inclure les mises à jour suggérées. Merci pour le code initial.

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