Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Java EE & AJAX] Bonne pratique / conseils

Sujet résolu
    8 août 2013 à 9:01:51

    Bonjour les "zeros",

    J'aurais besoin d'un avis / conseil.

    Dans l'appli que je développe actuellement, je me retrouve avec un formulaire, permettant la sélection d'un expéditeur.  Un peu comme pour l'exemple dans le splendide tuto disponible sur ce site, lorsque l'on choisi un client pour une commande.

    Maintenant, je suis en phase de développement, et la liste de mes expéditeurs est très petite (pour raisons de test, une dizaine maximum).  Mais, une fois que nous serons en production, la liste contiendra rapidement plusieurs centaines de valeurs.

    Les utilisateurs finaux de l'application m'ont demandés si il était possible de réaliser un ou deux champs de recherche pour limiter la liste en question.  Afin de ne pas recharger la page, et de donner une impression de fluidité, j'ai donc pensé créer la liste de sélection par un appel AJAX.

    Les question qui se pose à moi :

    1. Il me faut donc, depuis mon script JS, faire appel à une URL, qui sera traitée par ma servlet correspondante ?  Ai-je encore besoin d'une page JSP à ce moment, ou puis-je directement ajouter les valeurs dans la variable HttpServletResponse ?

    2. Comment, ou plutôt, sous quelle forme est ce que je renvoi le dit résultat ?  Est-ce que je renvoi directement la liste des options qui seront alors injectée dans mon select ?  Dois-je renvoyer un tableau 2D contenant d'une part les clé / valeurs et réaliser un parsing des dites données dans mon script JS ?  Ce script JS, est-il capable d’interpréter les données Java (je dois être à côté de la plaque pour cette dernière question)?

    3. Existe-t-il une autre solution que l'AJAX pour éviter de devoir recharger toute la page de mon formulaire ?

    En vous remerciant tous pour les avis / conseils / remarques que vous accepterez de partager avec moi, je vous souhaite une bonne journée.

    -
    Edité par rthreis 8 août 2013 à 10:46:23

    • Partager sur Facebook
    • Partager sur Twitter
      8 août 2013 à 12:01:52

      Bon, je penses être sur la bonne voie.

      Je renvoi donc une partie de code générée depuis mon couple JSP / Servlet.

      Par contre, à présent, pas la moindre idée de comment remplacer le contenu de mon select.

      xhr.onreadystatechange = function(){
              if( xhr.readyState === 4 && xhr.status === 200){
                  var listExpediteurs = document.getElementById("listeExpediteurs");
                  listExpediteurs.InnerHTML = xhr.responseText;
              }
          };

      Le code renvoyé est :

      <option value="2">Ville de Malmedy - Informatique - Threis Raphael</option>
      <option value="4">Ville de Malmedy - Accueil - Servais Nathalie</option>
      


      En vous remerciant.

      • Partager sur Facebook
      • Partager sur Twitter
        8 août 2013 à 12:43:34

        Personnellement, comme je préfère découper le fonctionnement de façon strict, je me limite à des comportement basiques que je dois faire des requètes AJAX comme tu le décris:

        - la servlet appelé en Ajax ne doit que traiter la requète. Elle prend en entrées des paramètres, elle retourne des paramètres (pas d'HTML !).

        - Le moyen de transport doit donc être capable d'encapsuler ces données (ex: XML...). Je préfère personnelement le JSON, qui me semble beaucoup plus approprié. Surtout depuis JEE7, où le parsing JSON est désormais présent en standart (via javax.json.Json)

        - le javascript récupère le JSON et met à jour la page en conséquence.

        L'avantage évident d'un tel découpage est qu'il n'est pas nécessaire de modifier la servlet si la partie HTML doit évoluer (ex: on veut passer à une liste à partir de balises li / ul sélectionnables).

        Et inversement, on peut faire en sortie que la servlet retourne plus d'informations, sans modifier le code client si ce n'est pas nécessaire.

        Pour ce qui est de la modification du DOM (supprimer des options du select / en rajouter etc...) je préfère passer par des bibliothèques (jQuery au hasard ^^)

        • Partager sur Facebook
        • Partager sur Twitter
          8 août 2013 à 12:54:55

          Alors, tout cela me paraît fort chinois.

          Pour ma part, j'ai la JSP suivante :

          <%@page contentType="text/html" pageEncoding="UTF-8"%>
          <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
          <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
          <c:set var="now" value="<%=new java.util.Date()%>" />
          <!DOCTYPE html>
          <html>
              <head>
                  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                  <link type="text/css" rel="stylesheet" href="<c:url value="../inc/form.css"/>" />
                  <script src="<c:url value="/inc/myscripts.js" />"></script>
                  <title>Ajout d'un courrier - AGMD</title>
              </head>
              <body>
                  <c:import url="../inc/menu.jsp" />
                  
                  <fieldset>
                      <legend>Ajout d'un courrier</legend>
                      <form method='POST' action='ajoutCourrier' enctype="multipart/form-data" >
                          <fieldset class="half-left">
                              <legend>Informations du courrier</legend>
                              <c:if test="${ empty form.erreurs}">
                                  <span class="succes"><c:out value="${form.message}" /></span>
                              </c:if>
                              <c:if test="${ !empty form.erreurs}">
                                  <span class="erreur"><c:out value="${form.message}" /></span>
                              </c:if>
                                  <br>
                              <label for='idCourrier'>Numéro du courrier</label>
                              <input type='text' id='idCourrier' name='idCourrier' value="<c:if test="${ !empty form.erreurs}"><c:out value="${courrier.idCourrier}" /></c:if>" /><span class="erreur"><c:out value="${form.erreurs['idCourrier']}"/></span><br>
          
                              <label for='dateEntree'>Date entrée</label>
                              <input type='text' id='dateEntree' name='dateEntree'
                              value="<c:if test="${ empty courrier.dateEntree}"><fmt:formatDate type="date" pattern="yyyy-MM-dd" value="${now}" /></c:if><c:if test="${ !empty courrier.dateEntree}"><fmt:formatDate type="date" pattern="yyyy-MM-dd" value="${courrier.dateEntree}" /></c:if>" />
                              <span class="erreur"><c:out value="${form.erreurs['dateEntree']}"/></span><br>
          
                              <label for='dateCourrier'>Date du courrier</label>
                              <input type='text' id='dateCourrier' name='dateCourrier' value="<c:if test="${ !empty form.erreurs}"><c:if test="${ !empty courrier.dateCourrier}"><fmt:formatDate type="date" pattern="yyyy-MM-dd" value="${courrier.dateCourrier}" /></c:if></c:if>"/><span class="erreur"><c:out value="${form.erreurs['dateCourrier']}"/></span><br>
          
                              <label for='nosRefs'>Nos références</label>
                              <input type='text' id='nosRefs' name='nosRefs' value="<c:if test="${ !empty form.erreurs}"><c:out value="${courrier.nosRefs}" /></c:if>" /><span class="erreur"><c:out value="${form.erreurs['nosRefs']}"/></span><br>
                              
                              <label for='vosRefs'>Vos références</label>
                              <input type='text' id='vosRefs' name='vosRefs' value="<c:if test="${ !empty form.erreurs}"><c:out value="${courrier.vosRefs}" /></c:if>" /><span class="erreur"><c:out value="${form.erreurs['vosRefs']}"/></span><br>
          
                              <label for='service'>Service traitant</label>
                              <select name="service" id="service">
                                  <option value="0">Veuillez choisir le service...</option>
                              <c:forEach items="${services}" var="service">
                                  <option value="<c:out value="${service.key}"/>" <c:if test="${ service.key == courrier.service }" >selected="selected"</c:if> ><c:out value="${service.value}" /></option>
                              </c:forEach>
                              </select>
                              <span class="erreur"><c:out value="${form.erreurs['service']}"/></span><br>
          
                              <label for='sujet'>Sujet du courrier</label>
                              <input type='text' id='sujet' name='sujet' value="<c:if test="${ !empty form.erreurs}"><c:out value="${courrier.sujet}" /></c:if>" /><span class="erreur"><c:out value="${form.erreurs['sujet']}"/></span><br>
                              
                              <%-- <label for="fichierCourrier">Le courrier</label>
                              <input type="file" id="fichierCourrier" name="fichierCourrier"/><br> --%>
                          </fieldset>
                          <fieldset class="half-right">
                              <legend>Informations de l'expéditeur</legend>
                              
                              <c:if test="${ !empty expediteurs}">
                                  <label for="choixNouveauExpediteur">Nouvel expéditeur ? <span class="requis">*</span></label>
                                  <input type="radio" id="choixNouveauExpediteur" name="choixNouveauExpediteur" value="nouveauExpediteur" checked /> Oui
                                  <input type="radio" id="choixNouveauExpediteur" name="choixNouveauExpediteur" value="ancienExpediteur"  />Non
                                  <br/><br />
                              </c:if>
                              
                              <div id="nouveauExpediteur" name="nouveauExpediteur">
                                  <c:import url="/inc/inc_expediteur_form.jsp" />
                              </div>
                              <c:if test="${!empty expediteurs}">
                                  <div id="ancienExpediteur" name="ancienExpediteur">
                                      
                                      <label for="entiteRecherche">Entité</label>
                                      <input type="text" id="entiteRecherche" name="entiteRecherche" /><br>
                                      <label for="nomRecherche">Nom</label>
                                      <input type="text" id="nomRecherche" name="nomRecherche" /><br>
                                      <button type="button" onclick="getExpediteur();" >Recherche</button>
                                      <div id="divExpediteurs"></div>
                                      
                                      <select name="listeExpediteurs" id="listeExpediteurs">
                                          <option value="">Choisissez un expéditeur...</option>
                                          <c:forEach items="${ expediteurs }" var="mapExpediteur">
          
                                          <option value="${ mapExpediteur.idExpediteur }">${ mapExpediteur.entite} - ${mapExpediteur.departement} - ${ mapExpediteur.nom } ${ mapExpediteur.prenom }</option>
                                          </c:forEach>
                                      </select>
                                  </div>
                              </c:if>
                              
                          </fieldset>
                          <div>
                              <input type="submit" value="Enregistrer" />
                              <input type='reset' value='Vider les champs'/>
                          </div>  
                      </form>
                  </fieldset>
                  <script src="<c:url value="/inc/jquery.js"/>"></script>
                  <script>
                      jQuery(document).ready(function(){
                          /* 1 - Au lancement de la page, on cache le bloc d'éléments du
                          formulaire correspondant aux clients existants */
                          $("div#ancienExpediteur").hide();
                          /* 2 - Au clic sur un des deux boutons radio "choixNouveauClient", on
                          affiche le bloc d'éléments correspondant (nouveau ou ancien client) */
                          jQuery('input[name=choixNouveauExpediteur]:radio').click(function(){
                              $("div#nouveauExpediteur").hide();
                              $("div#ancienExpediteur").hide();
                              var divId = jQuery(this).val();
                              $("div#"+divId).show();
                          });
                      });
                  </script>
              </body>
          </html>
          

          Je souhaite modifier la liste nommée "listeExpediteurs".  Pourquoi, car dans le futur, elle contiendra plusieurs centaines d'éléments, et donc permettre de sélectionner plus rapidement l'élément voulu.

          J'ai également le code suivant au niveau du traitement JS :

          /* 
           * To change this template, choose Tools | Templates
           * and open the template in the editor.
           */
          
          function getXMLHttpRequest(){
              var xhr = null;
          
              if (window.XMLHttpRequest || window.ActiveXObject) {
                  if (window.ActiveXObject) {
                      try {
                          xhr = new ActiveXObject("Msxml2.XMLHTTP");
                      } catch(e) {
                          xhr = new ActiveXObject("Microsoft.XMLHTTP");
                      }
                  } else {
                      xhr = new XMLHttpRequest(); 
                  }
              } else {
                  alert("Votre navigateur ne supporte pas l'objet XMLHTTPRequest...");
                  return null;
              }
              return xhr;
          }
          
          function getExpediteur(){
              var xhr = getXMLHttpRequest();
              
              alert("Dans la fonction getExpediteur");
              
              // utiliser la fonction encodeURIComponent(<la chaine>);
              
              var rechercheEntite = encodeURIComponent(document.getElementById("entiteRecherche").value);
              var rechercheNom = encodeURIComponent(document.getElementById("nomRecherche").value);
              
              xhr.open("POST", "/agmd/courrier/getExpediteurs", true);
              xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
              xhr.send("entiteRecherche="+rechercheEntite+"&nomRecherche="+rechercheNom);
              
              xhr.onreadystatechange = function(){
                  if( xhr.readyState === 4 && xhr.status === 200){
                      var listExpediteurs = document.getElementById("listeExpediteurs");
                      listExpediteurs.InnerHTML = xhr.responseText;
                  }
              };
              
          }
          

          Pour ce dernier, je n'ai pas la moindre idée de savoir si il est optimisé ou non.

          La fonction appel donc une page de mon appli, gérée par une Servlet, qui au final, renverra ceci par le biais d'une JSP :

          <%@page contentType="text/html" pageEncoding="UTF-8"%>
          <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
          <c:forEach items="${ expediteurs }" var="mapExpediteur">
              <option value="${ mapExpediteur.idExpediteur }">${ mapExpediteur.entite} - ${mapExpediteur.departement} - ${ mapExpediteur.nom } ${ mapExpediteur.prenom }</option>
          </c:forEach>

          Je souhaite donc, remplacer les options déjà présente dans ma première JSP, par le contenu récupéré dans cette dernière JSP.

          Tu parles dans ton post, de JSON, jQuery et autre, mais le soucis, je n'y connais rien.  A part savoir qu'il s'agisse de "quelque chose" utilisé dans le monde du WWW (de manière très générale), je ne sais pas ce que c'est.



          -
          Edité par rthreis 8 août 2013 à 12:55:11

          • Partager sur Facebook
          • Partager sur Twitter
            8 août 2013 à 13:31:56

            Bon,

            J'ai résolu mon soucis.

            InnerHTML ne fonctionne pas.  Il faut utiliser innerHTML, soit :

            function getExpediteur(){
                var xhr = getXMLHttpRequest();
                
                document.getElementById("listeExpediteurs").options.length = 0;
                
                // utiliser la fonction encodeURIComponent(<la chaine>);
                
                var rechercheEntite = encodeURIComponent(document.getElementById("entiteRecherche").value);
                var rechercheNom = encodeURIComponent(document.getElementById("nomRecherche").value);
                
                xhr.onreadystatechange = function(){
                    if( xhr.readyState === 4 && xhr.status === 200){
                        document.getElementById("listeExpediteurs").innerHTML = xhr.responseText;
                    }
                };
                
                xhr.open("POST", "/agmd/courrier/getExpediteurs", true);
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xhr.send("entiteRecherche="+rechercheEntite+"&nomRecherche="+rechercheNom);
            }

            Ce code est loin d'être optimisé, mais il est fonctionnel.

            • Partager sur Facebook
            • Partager sur Twitter
              8 août 2013 à 13:38:03

              Le JSON est un objet javascript. Son avantage est de pouvoir transiter dans les flux AJAX, et de pouvoir être utilisé directement par le javascript client.

              Plutôt qu'un long discourt, un peu de code pour y voir plus clair ;)

              La servlet de recherche:

              @WebServlet(name="SearchServlet", urlPatterns = {"/search"})
              public class SearchServlet extends HttpServlet {
              
                  @EJB
                  private IExpediteurDao expediteurDao;
              
                  @Override
                  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              
              		// On récupère les critères de recherche
                      String entity = request.getParameter("entity");
              		String name = request.getParameter("name");
              		
              		// On fait la recherche dans la BDD
              		List<Expediteur> list = expediteurDao.search(entity, name);
              		
              		// On construit le tableau JSON des résultat
              		JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
              		
              		for(Expediteur expediteur : list) {
              		
              			// On construit l'objet JSON pour l'expediteur
              			JsonObjetBuilder objectBuilder = Json.createObjectBuilder();
              			objectBuilder.add("id", expediteur.getId());
              			objectBuilder.add("entity", expediteur.getEntite());
              			objectBuilder.add("department", expediteur.getDepartment());
              			objectBuilder.add("lastName", expediteur.getLastName());
              			objectBuilder.add("firstName", expediteur.getFirstName());
              		
              			arrayBuilder.add(objectBuilder);
              		}
              		
              		// On indique que le flux est du JSON
              		response.setContentType("application/json; charset=UTF-8");
              		
              		// On renvois le résultat
              		response.getWriter().write(arrayBuilder.build().toString());
                  }
              
              }

              Le javascript :

              function getExpediteur() {
              
              	var rechercheEntite = jQuery('#entiteRecherche').val();
              	var rechercheNom = jQuery('#nomRecherche').val();
              	
              	// Autant utiliser les fonctions AJAX de jQuery
              	jQuery.ajax({
              		type: 'GET',
              		url: 'search', // L'URL de la servlet de recherche
              		data: { // Les parmètres envoyées
              			entity: rechercheEntite,
              			name: rechercheNom
              		},
              		success: function(result) { 
              		
              			// On vide la liste
              			jQuery('#listeExpediteurs').empty();
              		
              			// Pour chaque résultat du tableau
              			jQuery.each(result, function(index, value){
              			
              				// on retrouve les paramètres qu'on avait fixé via l'API Json dans la servlet
              				var id = value.id;
              				var label = value.entity + ' - ' + value.department + ' - ' + value.lastName + ' ' + value.firstName;
              			
              				// On rajoute une nouvelle option
              				jQuery('#listeExpediteurs').append('<option value="' + id + '">' + label + '</option>');				
              			});
              		
              		}
              	});
              
              }

              Note: j'ai tapé le code dans un bloc note; ce n'est donc pas testé ! Mais le principe est là.


              -
              Edité par Sebajuste 8 août 2013 à 13:41:55

              • Partager sur Facebook
              • Partager sur Twitter
                9 août 2013 à 15:06:54

                Wahou, je ne comprend pas grand choses dans l'immédiat, mais cela fonctionne.  A quelques correction prêt, tout est bon.

                En te remerciant pour cette solution.

                Par contre, j'ai cru comprendre un jour, qu'il fallait toujours séparer le côté présentation et le côté génération des données.

                Je ne sais donc pas, si la solution proposée, respecte bien cette distinction.

                • Partager sur Facebook
                • Partager sur Twitter
                  9 août 2013 à 16:28:33

                  Très bonne remarque. Et pour te rassurer, biensûr que la solution proposée respecte la séparation des couches :)

                  Comme tu le vois, la servlet de recherche ne gère absolument pas l'affichage. Pas d'html, ni de redirection vers une JSP. Elle ne fait que le lien entre la base de données (via la couche DAO) et le script avec (via le format JSON). C'est juste une porte d'entrée.

                  C'est à la page web, coté client, qu'il convient de traiter les données reçues (via l'AJAX) et de les insérer dans la page HTML. Ce que fait le javascript ici.

                  Si tu as d'autres questions, n'hésites pas.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  [Java EE & AJAX] Bonne pratique / conseils

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown