Un premier appel Ajax avec Prototype.js : Ajax.Request

20 janvier 2007Ajax, prototype.js
 Cet article a été rédigé il y a plusieurs années et peut ne plus être tout à fait à jour…

prototype.js est une bibliothèque de fonctions JavaScript écrite par Sam Stephenson ; elle est utilisée comme base par le framework Ruby on Rails, ainsi que par la bibliothèque script.aculo.us.

Prototype.js inclut tellement de fonctionnalités qu'il n'est pas possible de toutes les présenter en un article.
Je me limiterai ici uniquement à une partie des fonctionnalités Ajax : celles constituant la base de toute requête, ainsi que celles facilitant la tâche au développeur par rapport à une utilisation directe de l'objet XMLHttpRequest.

D'autres articles suivront, allant plus loin que ce à quoi je me limiterai pour celui-ci, ne voulant pas le rendre trop long.


Pour information, une référence prototype.js est disponible en ligne : Référence prototype.js.
Et voici la Documentation de l'API de prototype.js
Je ne peux que vous encourager à les parcourir !

prototype.js

Pour cet article, j'utiliserai la version 1.5.0 de la bibliothèque prototype.js[1].

Pour faciliter les choses, vous pouvez la télécharger ici :

La version "compressée" correspond à la version "standard", qui a été compressée grâce à l'outil JSMin, qui - pour résumer - supprime les espaces blancs inutiles d'un fichier JavaScript.

Elle présentent toutes deux les mêmes fonctionnalités, mais la version "compressée" est de taille moins importante. Elle est donc plus rapide à télécharger pour vos utilisateurs - au prix d'une grosse perte en terme de lisibilité[2].

Quelques méthodes utilitaires

Avant de commencer, je vais introduire quelques fonctions/méthodes fournies par prototype.js, que j'utiliserai dans mes exemples.
(Ces fonctions étant présentes dans la librairie, autant les utiliser ; cela rend notre code plus court, et c'est la bibliothèque qui gère les éventuels problèmes de compatibilité entre navigateurs)

$()

La fonction $() est un alias - raccourci - de la fonction document.getElementById().
Comme la fonction correspondante du DOM, elle retourne l'élément qui a l'identifiant ("id") passé en paramètre.

Par exemple :

$('test').innerHTML = '<p>Hello World !</p>';

est identique à :

document.getElementById('test').innerHTML = '<p>Hello World !</p>';

La fonction $() va tout de même un peu plus loin, puisqu'elle accepte en paramètre une liste d'identifiants d'éléments - auquel cas elle retournera un tableau (Array) contenant les éléments demandés.

Element.show ; Element.hide

Les méthodes Element.show(element) et Element.hide(element) permettent respectivement d'afficher et de masquer un élément.

Les deux lignes suivantes sont équivalentes[3] :

Element.show('chargement');

et

document.getElementById('chargement').style.display = '';

Dans les deux cas, le "display: none" disparaît de la liste de styles de l'élement chargement.

Première requête Ajax avec Prototype.js

Comme lorsque nous avons effectué une requête en utilisant directement l'objet XHR, nous avons besoin de trois composantes :

  • Une page cliente : HTML, XHTML, ...a
  • Quelques lignes de JavaScript exécutant la requête, et obtenant le résultat.
  • Et un programme côté serveur, qui sera appelé par la requête Ajax - par exemple, un script PHP - il s'appellera "test2.php".

Page cliente

La page cliente que j'utiliserai pour nos exemples est des plus simples :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
    <title>AJAX : Exemple de client</title>
    <script type="text/javascript" src="prototype-1.5.0.js"></script>
    <script type="text/javascript" src="test2.js"></script>
</head>
<body>
    <p>
        <a href="" onclick="gestionClic(); return false;">
            Cliquez ici !
        </a>
    </p>
    
    <div id="resultat">&nbsp;</div>
    
</body>
</html>

Quelques points :

  • Inclusion de prototype.js
  • Inclusion du fichier test2.js, dans lequel se trouvera mon code JavaScript
  • Un lien qui déclenchera l'exécution d'une fonction de ce code JS
  • Et un élément où sera affiché le résultat - notez qu'il a pour identifiant "resultat"


Ajax.Request

C'est la classe Ajax.Request qui permet, avec prototype.js, de réaliser des requêtes Ajax.

Pour tous mes exemples, je développerai une fonction nommée gestionClic - celle appelée lors d'un clic sur le lien présenté un peu plus haut.

Voici sa première version :

function gestionClic()
{
    var url = './test2.php';
    var myAjax = new Ajax.Request(
            url, 
            {
                method: 'get', 
                onComplete: gestionReponse
            });
} // gestionClic()

Une requête Ajax est toujours émise vers une URL ; c'est le premier paramètre attendu par la méthode Ajax.Request().

Le second paramètre permet de définir un grand nombre d'options, définissant des paramètres spécifiques, ou, surtout, quelles méthodes seront appelées lors du cycle de vie de l'objet XMLHttpRequest.

Ici, nous utilisons deux paramètres :

  • method permet de définir quelle méthode HTTP sera employée par la requête. En général, on utilise 'GET' ou 'POST', la seconde étant la méthode par défaut si aucune n'est spécifiée.
  • onComplete permet d'indiquer quelle fonction devra être exécutée une fois l'appel Ajax terminé. Typiquement, cette fonction exploitera les données obtenues via la-dite requête.
    Ici, une fois la requête terminée, c'est la fonction gestionReponse qui sera appelée - nous en présenterons une version un peu loin.


PHP côté serveur

Côté serveur, encore une fois, j'utiliserai le PHP.

Le programme développé pour cet exemple est des plus simples : il n'effectue qu'un affichage :

<?php
    header('Content-Type: text/html; charset: UTF-8');
    echo 'Hello World!';
?>


Obtention et utilisation du résultat

Revenus côté client, il est maintenant temps d'exploiter le retour de notre requête Ajax.
Pour cela, conformément à ce qui a été défini lors de l'appel de Ajax.Request, voici la fonction gestionReponse :

function gestionReponse(xhr)
{
    if (xhr.status == 200)
    {
        $('resultat').innerHTML = xhr.responseText;
    }
    else
    {
        $('resultat').innerHTML = xhr.status;
    }
}

Le (premier[4]) paramètre passé par Ajax.Request à la fonction définie comme déclenchée une fois la requête terminée (événement onComplete) est l'objet XMLHttpRequest utilisé pour effectuer la requête.

Puisque nous avons branché cette fonction sur l'événement onComplete, elle sera appelée lorsque la requête Ajax sera terminée, quel que soit le code statut HTTP renvoyé par le serveur.

Dans le cas où tout s'est bien passé, la propriété status de l'objet XMLHttpRequest reçu en paramètre vaut 200 ("OK") :

Si l'URL appelée correspond à une page existante, accessible, et ne causant pas d'erreur :

var url = './test2.php';

Alors, nous afficherons à l'écran le contenu renvoyé par le serveur - en l'occurence, "Hello World!".

Pour cela, nous utiliserons la propriété responseText de l'objet XHR, en vue d'obtenir les données "affichées" par le programme PHP.

Mais si l'URL appelée correspond à une page inexistante - par exemple :

var url = './test-404.php';

Alors, nous afficherons le code statut HTTP renvoyé par le serveur - ici, puisque la page n'existe pas, ce sera "404".
(Nous pourrions aussi utiliser la propriété statusText, pour afficher le message correspondant - en l'occurence, "Not Found")


Passer des paramètres

Est-il vraiment nécessaire de souligner qu'effectuer une requête Ajax n'est pas très utile si ce qu'elle renvoi ne dépend pas de paramètres que vous lui passez ?

Le passage de paramètre peut se faire en GET ou en POST, comme présenté ci-dessous :

Passer des paramètres en GET

prototype.js accepte deux syntaxes pour le passage de paramètres :

  • Sous forme d'une chaîne de caractères : 'param1=valeur1&param2=valeur2&param3=valeur3'
  • Et sous forme d'un tableau associatif : {param1: 'valeur1', param2: 'valeur2', param3: 'valeur3'}

La seconde méthode est celle qui est recommandée - il faut avouer qu'un tableau associatif est plus lisible, et plus facile à manipuler, qu'une chaîne de caractères.

En GET, les paramètres sont passés comme option, celle-ci étant nommée parameters :

function gestionClic()
{
  var url = './test2.php';
  var myAjax = new Ajax.Request(
      url,
      {
        method: 'get',
        parameters: 'param1=valeur1&param2=valeur2&param3=valeur3',
        onComplete: gestionReponse
      });
} // gestionClic()

Ou avec la seconde écriture :

function gestionClic()
{
  var url = './test2.php';
  var myAjax = new Ajax.Request(
      url,
      {
        method: 'get',
        parameters: {param1: 'valeur1',
                     param2: 'valeur2', 
                     param3: 'valeur3'},
        onComplete: gestionReponse
      });
} // gestionClic()

Dans les deux cas, en ayant comme programme côté serveur ceci :

<?php
    header('Content-Type: text/html; charset: UTF-8');
    echo '<pre>' . print_r($_GET, true) . '</pre>';
?>

Nous obtiendrons l'affichage suivant - en supposant que la fonction appelée en fin de requête Ajax fait, comme plus haut, un simple affichage écran de ce qui a été renvoyé par le serveur :

> Array
> (
>     [param1] => valeur1
>     [param2] => valeur2
>     [param3] => valeur3
> )

Nous voyons bien que les trois paramètres ont été reçus par le serveur, et que l'association entre les noms de paramètres et leurs valeurs a été conservée.


Et passer des paramètres en POST

Normalement, une requête en GET ne devrait pas causer de modification de données... Et les navigateurs conservent parfois les données de requêtes GET en cache - plus fréquemment qu'avec des requêtes POST.

En POST, nous disposons des deux mêmes possibilités qu'en GET pour passer des paramètres... Mais ceux-ci peuvent être passés à Ajax.Request soit via l'option parameters - comme plus haut - soit par l'option postBody.
Note : si la seconde est utilisée, la première est ignorée.

En général, on utilise postBody, pour passer des données en POST :

function gestionClic()
{
  var url = './test2.php';
  var myAjax = new Ajax.Request(
      url,
      {
        method: 'post',
        postBody: 'param1=valeur1&param2=valeur2&param3=valeur3',
        onComplete: gestionReponse
      });
} // gestionClic()

Avec comme programme serveur le suivant :

<?php
    header('Content-Type: text/html; charset: UTF-8');
    echo '<pre>' . print_r($_POST, true) . '</pre>';
?>

Nous obtiendrons exactement le même affichage que tout à l'heure, lorsque nous passions nos données en GET.


Attention : le passage de paramètres en utilisant postBody ne peut pas se faire en utilisant un tableau associatif !
L'écriture suivante est incorrecte :

postBody: {param1: 'valeur1',
           param2: 'valeur2', 
           param3: 'valeur3'}

Avec cette écriture, le serveur ne reçoit aucune donnée !

Une solution pour contourner ce problème est d'utiliser la fonction $H(), qui permet de créer un objet Hash à partir d'un objet en notation litérale, et d'utiliser la méthode toQueryString() de la classe Hash pour convertir cet objet en une chaîne telle qu'acceptée par l'option postBody, de la manière suivante :

postBody: $H({param1: 'valeur1',
              param2: 'valeur2', 
              param3: 'valeur3'}).toQueryString()

Et là, nous obtenons le fonctionnement attendu.


Aller plus loin

Quelques articles viendront bientôt, qui constitueront une suite logique à celui-ci :

Je mettrai cette liste à jour au fur et à mesure que les articles seront en ligne, afin de rajouter les liens correspondant.

N'hésitez pas à vous abonner au flux RSS (voir colonne de droite), pour être informé des nouveaux articles parus sur ce blog !


Mises à jour

  • MAJ le vendredi 23 février 2007 : Ajout de liens à la partie Aller plus loin.


Notes

[1] Quand j'ai commencé à rédiger ceci, la version la plus récente de prototype.js était la 1.5.0_rc2 ; mais la 1.5.0 est sortie avant que cet article ne soit terminé

[2] Typiquement, vous pouvez utiliser la version non compressée lorsque vous développez, et la version compressée une fois votre application en production. Cela dit, testez tout de même votre application avec la version compressée avant la mise en production - on n'est jamais trop prudent !

[3] Elles sont équivalentes que ce soit dans l'effet - l'élément est affiché - ou dans le fonctionnement : la première est un raccourci pour la seconde

[4] Nous verrons dans un autre article qu'il peut y en avoir un second, dans le cas où le serveur avait retourné des données au format JSON