Ajax (Asynchronous Javascript And XML)

Principe de base : on envoie une requête asynchrone au serveur, et les résultat de cette requête sert à modifier des éléments de la page courante.

Ajax "à la main"

Ajax et dojo

Dojo est une bibliothèque très riche (http://dojotoolkit.org/), qui contient beaucoup d'éléments pour réaliser des effets à base d'ajax. Elle a aussi pour particularité de proposer des modes dégradés, qui essaient de fonctionner même si le navigateur n'est pas assez récent.

Exemple de complétion en Dojo

Voici le code :
<script type="text/javascript" src="js/dojo/dojo.js"></script>
	<script type="text/javascript">
		// Chargement de l'objet ComboBox :
		dojo.require("dojo.widget.ComboBox");
	</script>
	
	<select dojoType="ComboBox" mode="remote" dataUrl="dico/completeDojo?debut=%{searchString}"
		autoComplete="true"
		name="dico2" 
	></select>

et la partie intéressante de la servlet :
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String debut= request.getParameter("debut");
		if (debut== null)
			debut= "";
		response.setContentType("text/plain");
		response.setCharacterEncoding("UTF-8");
		PrintWriter w = response.getWriter();
		w.print("[");
		boolean virgule= false;
		int taille= 0;
		for (String m: dictionnaire.tailSet(debut)) {
			// On s'arrête si on a plus de 50 réponses. 
			if (++taille > 50)
				break;
		
			// Si le mot commence bien par début, l'ajouter.
			// Sinon: terminer la boucle.
			if (m.startsWith(debut)) {
				if (virgule)
					w.print(",\n");
				w.print("[\"");
				w.print(protectHTML(m));
				w.print("\",\"");
				w.print(protectHTML(m));
				w.print("\"]");
			}
				else
				break;
			virgule= true;
		}
		w.print("]");
		w.close();
	}

Utilisation "basique" d'ajax à partir de Dojo

Pour les requêtes Ajax générales, Dojo propose la méthode bind, qui prend comme argument un tableau associatif

La documentation de bind se trouve à l'adresse http://manual.dojotoolkit.org/io.html


<form>
	<input id="debutMotDojo" name="debutMotDojo" onkeyup="autoCompleteManuelDojo()">
	<select id='motDojo' name="motDojo" style="width: 16em"></select>
</form>

<script type="text/javascript">
dojo.require("dojo.io.*");
function autoCompleteManuelDojo() {

	var debutMot= document.getElementById('debutMotDojo').value;
	if (debutMot.length > 2) {
		dojo.io.bind({
			url : "dico/complete?debut="+ encodeURIComponent(debutMot),
			load : chargerCompletionDojo,
			mimetype: "text/html"
			}
		);
	}	
}

// type : load ou error
// data : texte de la réponse
// evt : événement DOM
function chargerCompletionDojo(type,data,evt) {
	document.getElementById('motDojo').innerHTML= data;
}
</script>

Ajax et scriptaculous

scriptaculous et prototype : deux bibliothèques "légères" d'éléments Ajax. (http://script.aculo.us/).

Manipulation directe d'ajax

L'équivalent du "bind" de dojo. Exemple : mise à jour d'une liste à partir du contenu d'une autre liste.
	
	<!-- Javascript for linking autocompletion to actual page elements -->
<script type="text/javascript">
<script type="text/javascript" src="<c:url value='/js/prototype.js'/>"></script>

function updateRelationObjects() {
	var url= '<c:url value="/appli/subRelation"/>'+ "?relationType=" +$F('relationName');
	// Mets à jour le champ d'id "relationObjects" à partir des données provenant de l'URL.
	// Le contenu renvoyé par l'URL remplace le contenu du select.
	new Ajax.Updater('relationObjects', url, {asynchronous:true});
}

</script>

	<select name='signSearch.relationName' id="relationName"
			onchange="javascript:updateRelationObjects()">
			<option value="NONE"><none></option>
			<option value="POSTURE">posture</option>
			<option value="ACTION">action</option>
		</select>
		
		<select name='signSearch.relationObject' id='relationObjects'>
			<option value="NONE"><none></option>
		</select>

Autocompletion

Voir http://wiki.script.aculo.us/scriptaculous/show/Ajax.Autocompleter.

Différence avec les autres : le protocole est POST.

Pour mettre en place l'autocomplétion, il faut définir un champ, et une division (div) qui recevra la liste des complétions possibles :

	<form>
		<!-- Le champ  -->
		<input type="text" id="scAutocomplete" name="scParametre"></inp
		<!-- La liste des données pour la complétion -->
		<div id="scChoix" class="autocomplete"></div>
	</form>
Il convient ensuite, dans le fichier de style CSS, de placer les informations utiles pour cacher et montrer la liste de complétion.
/* Pour la completion Ajax */

    div.autocomplete {
      position:absolute;
      width:250px;
      background-color:white;
      border:1px solid #888;
      margin:0px;
      padding:0px;
    }
    div.autocomplete ul {
      list-style-type:none;
      margin:0px;
      padding:0px;
    }
    div.autocomplete ul li.selected { background-color: #ffb;}
    div.autocomplete ul li {
      list-style-type:none;
      display:block;
      margin:0;
      padding:2px;
      height:32px;
      cursor:pointer;
    }
Enfin, on appelle une méthode de Scriptaculous pour mettre en place l'auto complétion:
	<script type="text/javascript">
		// premier argument : l'ID du champ à compléter
		// second : l'ID de la division qui reçoit les données d'autocomplétion
		// options :
		//    paramName: le nom du paramètre passé à la servlet (récupéré par getParameter())
		//	  minChars : la complétion n'a lieu qu'a partir de deux caractères.
		new Ajax.Autocompleter("scAutocomplete", "scChoix", "dico/completeSC", {'paramName' : 'debut', minChars: 2});
	</script>
La servlet doit renvoyer (à partir d'une requête POST) une liste HTML :
	<ul>
		<li>valeur1</li>
		<li>valeur2</li>
		<li>valeur3</li>
	</ul>	
Elle a donc pour nous la forme :
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// Note : scriptaculous utilise a priori UTF-8. Cette déclaration permet
		// à notre servlet de décoder correctement les caractères accentués.
		request.setCharacterEncoding("UTF-8");
		String debut= request.getParameter("debut");
		response.setContentType("text/plain");
		response.setCharacterEncoding("UTF-8");
		PrintWriter w = response.getWriter();
		
		System.out.println("recut "+ debut);
		w.println("<ul>");
		for (String m: dictionnaire.tailSet(debut)) {
			// Si le mot commence bien par début, l'ajouter.
			// Sinon: terminer la boucle.
			
			if (m.startsWith(debut)) {
				w.print("<li>");
				w.print(protectHTML(m));
				w.print("</li>");
			}
				else
				break;
		}
		w.println("</ul>");
		w.close();
	}