Catalyst & Ajax

user_icon admin | icon2 Catalyst | icon4 30/10/2007 12h35| Type doc: article| Type File: txt| icon3 No Comment

Catalyst et Ajax


1. Ajax

Ajax signifie "Asynchronous Javascript and XML", mais encore ???

En fait il s'agit de l'assemblage de technologies largement éprouvées que sont le Javascript et le XML, le but étant la mise à jour asynchrone d'une portion de page HTML. Grossièrement on peut dire que lors de l'apparition d'un évenement (clic, passage de la souris au-dessus d'un élément, ...) une requete est transmise à un serveur, ce dernier produit une réponse qui est renvoyée de manière asynchrone au client. Ce dernier récupère la réponse et met à jour la portion de la page HTML sans la recharger en totalité.

Javascript interrogation coté Client -> script coté serveur -> javascript récupération + mise à jour coté client

Dans ce chapitre je décrirai le moyen de faire de l'Ajax en Javascript brut et à l'aide de divers toolkits Javascript. Je m'aiderai de la librairie Perl Catalyst pour les bases de l'application mais les exemples donnés fonctionnent sans ce dernier.

2. Utilisation de XMLHttpRequest

Nous souhaitons modifier de manière asynchrone le contenu d'un champ de donnée en fonction d'un autre. Dans cet exemple nous multiplierons le contenu d'un champ par 2 et afficherons la réponse dans un autre champ et dans un 'div'.

En avant dans la création de notre petite application :

catalyst.pl myapp

cd myapp
./script/myapp_create.pl view TT TT
./script/myapp_create.pl controller ajax

On a donc créé une application 'myapp', une vue TT et un controleur nommé ajax .

Il nous faut simplement modifié le squelette du controleur ajax pour utiliser le template root/ajax.tt pour l'affichage

vi lib/myapp/Controller/ajax.pm

sub index : Private {
    my ( $self, $c ) = @_;

    $c->stash->{mydate}=`date`;
    $c->stash->{template}="ajax.tt";
    $c->forward('myapp::View::TT');
}

il ne nous reste plus qu'à créer le template root/ajax.tt comme ci-dessous:

<html>
<head>
        <title>Test ajax</title>

        <script type="text/javascript">
                var xhr;
                function gettoto(mytoto) {
                        xhr = new XMLHttpRequest();
                        xhr.onreadystatechange=processtoto;
                        xhr.open("GET","/getresulttoto?toto=" + mytoto);
                        xhr.send(null);
                }
                function processtoto(){
                        if (xhr.readyState == 4 ){
                                var data = xhr.responseText;
                                var resultat = data;
                                document.getElementById("result").value = resultat;
                        }
                 }
        </script>

</head>
<body>
        <p>Test Ajax le [% mydate %]</p>

        toto: <input onkeyup="gettoto(this.value)" type="text" name="toto"/><br>

        result: <input id="result" type="text" name="autre"/>

</body>
</html>

Pour faire simple, on constate que lorsque l'on entre des chiffres dans le champ 'toto' ( onkeyup ) la fonction gettoto est exécutée.

gettoto créé alors une requête XMLHttpRequest à destination de getresulttoto en lui fournissant le contenu du champ toto . Lorsque le serveur renvoi le resultat de la requête (onreadystatechange) la fonction processtoto est exécutée. Elle a pour seul but d'afficher le resultat dans le champ result . On a donc une fonction pour l'émission de la requête ( gettoto ) et une pour la restitution du resultat ( processtoto ).

Il nous manque que le script de calcul getresulttoto ( il s'agit d'un simple controleur qui servira de multiplicateur)

./script/myapp_create.pl controller getresulttoto

Modifions sa méthode index comme suit:

sub index : Private {
    my ( $self, $c ) = @_;

    my $resultat=$c->req->params->{toto}*2;
    $c->res->output( $resultat );
}

Et c'est tout :)

Lorsque l'on entre un chiffre dans le champ toto celui-ci est multiplié par 2 est affiché dans le champ result sans que la page n'ai été rechargée.(L'heure affichée n'a pas changé)

Plutot que de modifier le contenu d'un champ 'input' nous aurions pu modifier le contenu d'un champ 'div' comme ceci:

...
var Element=document.getElementById("remplace");
Element.setAttribute("style", "background: red");
Element.replaceChild(document.createTextNode(resultat),Element.childNodes[0]);
...


<div id="remplace">En attente de l appel Ajax</div>

Le résultat n'est pas très impressionnant mais montre bien qu'une requête est faite en arrière plan. Malheureusement celà ne fonctionne qu'avec les navigateurs de type Mozilla et Safari. Avec IE5 et plus la requête devient:

var xhr = new ActiveXObject("Microsoft.XMLHTTP");

Arghhhh .... va-t-il nous falloir créer des scripts spécifiques à chaque navigateur ?

De même dans notre exemple la gestion des erreurs n'est pas prise en compte, nous pourrions la traiter comme ceci:

if (xhr.readyState == 4 ){
   if ( xhr.status == 200 ){
        ....
   }
   else{
        ....
   }
}

La latence du réseau n'est pas non plus traitée :(

3. Toolkit Dojo

Heureusement il existe des librairies Javascript qui vont nous aider dans cette tâche. ( Prototype , Dojo , JQuery ...). Dans ce chapitre nous étudierons ' Dojo ' dans sa version 0.9. Un comparatif des divers toolkits est disponible sur ajaxexperience.techtarget.com .

Dojo fourni des composants permettant notamment de faciliter l'emploi d'Ajax ( dojo.xhrGet ), rendant le site plus réactif et attractif. Il permet de nombreuses autres facilités qui ne seront pas abordées dans ce chapitre. Voir les possibilités : http://example.com/js/dojo-0.9.0/dijit/themes/themeTester.html

Pour prendre en compte les fonctions de Dojo , il nous faut dabord l'intégrer à nos pages HTML (dans la balise head). Pour cela téléchargeons ce dernier

cd /tmp/
wget http://download.dojotoolkit.org/release-0.9.0/dojo-release-0.9.0.tar.gz
tar xvzf dojo-release-0.9.0.tar.gz

Dans notre application précédente créons le répertoire root/static/js dans lequel nous recopions le toolkit.

cd -
mkdir root/static/js
mv /tmp/dojo-release-0.9.0 root/static/js/

Il nous suffit maintenant d'ajouter la ligne suivante à nos templates pour en profiter.

<script language="Javascript" type="text/javascript" src="/static/js/dojo-release-0.9.0/dojo/dojo.js">

</script>

Reprenons notre exemple avec un nouveau controleur ' ajax2 ' et un nouveau template ' ajax2.tt ' qui cette fois utilisera Dojo.

./script/myapp_create.pl controller ajax2

Modifions la méthode index de ajax2.pm comme nous l'avons fait pour ajax.pm .

sub index : Private {
    my ( $self, $c ) = @_;

    $c->stash->{mydate}=`date`;
    $c->stash->{template}="ajax2.tt";
    $c->forward('myapp::View::TT');
}

Dans notre nouveau template root/ajax2.tt nous utiliserons la méthode xhrGet de Dojo:

<html>
 <head>
  <title>Hello, Ajax2 world!</title>

  <script type="text/javascript"
    src="http://localhost:3000/static/js/dojo-release-0.9.0/dojo/dojo.js"></script>

  <script type="text/javascript">
        function gettoto(mytoto) {
                var kw = {
                        url: "/getresulttoto?toto=" + mytoto,

                        load: function(response, ioArgs){
                              dojo.byId("result").value = response;

                              var Element=dojo.byId("remplace");
                              // Element.setAttribute("style", "background: red");

                              Element.style.background = "red";
                        },
                        error: function(response, ioArgs){
                              console.error("HTTP status code: ", ioArgs.xhr.status);
                        },
                        timeout: 2000,
                };
                dojo.xhrGet(kw);
        }
  </script>
 </head>

<body>
        <p>Test Ajax le [% mydate %]</p>

        toto: <input onkeyup="gettoto(this.value)" type="text" name="toto"/><br>
        result: <input id="result" type="text" name="autre"/>

        <div id="remplace">En attente de l appel Ajax</div>
</body>
</html>

Dojo se chargera de rendre notre script compatible avec tout les navigateurs et traitera les erreurs.

Propre et efficace.

4. Toolkit Prototype

Comme Dojo, Prototype est un toolkit javascript permettant de nombreuses fantaisies. Couplé avec Scriptaculous (Pour les effets visuels) il devient le toolkit Ultime.

Seul l'appel Ajax sera traité ici et voilà sans attendre comment le mettre en place.

var url = 'test.pl';

var params = 'par1=val1';
var dest = 'mydiv';
var myAjax = new Ajax.Updater(dest, url, {method: 'get', parameters: params});

Lors de l'appel Ajax un requête GET est exécutée sur l'url test.pl en y passant les paramêtres CGI par1=val1 . Le résultat est placé dans ' mydiv '.

Un exemple simple va nous mettre de mieux en comprendre le mécanisme mais avant cela il nous faut installer les fichiers nécessaires à l'environnement Prototype . Nous utiliserons l'application réalisée au cours des chapitres précédents.


cd root/static/js
wget http://www.prototypejs.org/assets/2007/6/20/prototype.js
cd -

Voyons un exemple simple accessible par le controleur ajax4

./script/myapp_create.pl controller ajax4

Comme dans les chapitres précédents, modification de sa méthode index ( lib/myapp/Controller/ajax4.pm ) pour qu'il utilise le template root/ajax4.tt .

sub index : Private {
    my ( $self, $c ) = @_;

    $c->stash->{template}="ajax4.tt";
    $c->forward('myapp::View::TT');
}

Et enfin la création du template root/ajax4.tt :

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Ajax facile</title>
    <script type="text/javascript" src="/static/js/prototype.js"></script>

    <script type="text/javascript">
        Event.observe(window, 'load', init, false);
        function init(){
                Event.observe('toto', 'keyup', gettoto, false);
        }
        function gettoto(){
                var url = '/getresulttoto';
                var pars = 'toto='+escape($F('toto'));
                var target = 'remplace';
                var myAjax = new Ajax.Updater(target, url, {method: 'get', parameters: pars});
        }
    </script>

</head>
<body>
        <p>Test Ajax avec Prototype.js</p>
        <br />

    <form method="get" action="/getresulttoto" id="greeting-form">

        entrer un nombre: <input id="toto" type="text" name="toto"><br>
    </form>

    <div id="remplace">En attente de l'appel Ajax</div>

</body>
</html>

Le principe est le suivant:

  • Chargement du toolkit static/js/prototype.js

  • Le listener Event.observe exécute la fonction 'init' lorsque la fenêtre (window) est chargée (load). (Lors du chargement de la page)

  • Dans la fonction ' init ' un listener se charge de vérifier si une touche est enfoncée ' onkeyup '. Si c'est le cas la fonction gettoto est exécutée.

  • La fonction 'gettoto' fait un appel à l'url ' /getresulttoto ' en fournissant ' toto ' en paramêtre CGI. Le résultat est retourné dans la balise <div> dont l'id est ' remplace '.

5. Toolkit JQuery

JQuery est un framework Javascript permettant la manipulation d'ensemble d'élément du DOM avec beaucoup de facilité. Un élément du DOM pouvant être vu comme un noeud d'une page HTML isolé (comme un p ou a).

Lorsque l'on ouvre une nouvell balise HTML, on crée un élément du DOM. Tout ce qui se trouve à l'intérieur de cette balise est un enfant de cet élément du DOM. Un objet JQuery peut être considérer comme une boîte qui contient un ensemble d'éléments du DOM. Par exemple $("a.maclasse") est l'ensemble des balise 'a' dont la classe est 'myclasse'. Lorsque l'objet JQuery est défini on peut lui appliquer d'un coup toutes sortes d'opérations sur l'ensemble de ces éléments.

Par exemple pour ajouter une classe 'classe2' à tous les éléments de notre objet JQuery $("a.maclasse") :

$("a.maclasse").addClass("classe2")

On peut aussi chainer les opérations:

$("a.maclasse").hide()

qui aura pour effet de faire disparaitre les éléments impactés.

Détection des évènements : Imaginons que nous voulions modifier les éléments d'un objet JQuery lorsque l'on clique sur celui-ci:

$("p.cliquable").click(
  function(){
     $(this).addClass("on");
  }
)

=> lorsque l'on clique sur un paragraphe dont la classe est 'cliquable', nous lui ajoutons la classe 'on'

Pour plus d'informations sur JQuery :

Encore une petite remarque: Avant d'exécuter une 'requete' JQuery il est conseillé de s'assurer que le document est chargé entièrement (Le DOM en fait). Pour cela les requetes seront utilisé dans un bloc :


$(document).ready(function() {
   ...
}

ou son équivalent raccourci :

$(function() {
        // code à exécuter quand le DOM est chargé
});

On va maintenant voir ce qu'il en est des possibilités Ajax de JQuery mais avant toute chose installons cette librairie.

cd root/static/js/
wget http://jqueryjs.googlecode.com/files/jquery-1.2.1.js
cd -

Même exercice qu'avec les précédents toolkit : un multiplicateur par 2.

Création du controleur ajax5 et de son template associé root/ajax5.tt :


./script/myapp_create.pl controller ajax5

on modifie sa méthode index ( lib/myapp/Controller/ajax5.pm )

sub index : Private {
    my ( $self, $c ) = @_;

    $c->stash->{template}="ajax5.tt";
    $c->forward('myapp::View::TT');
}

Et enfin création du template root/ajax5.tt :

<html>
 <head>
  <title>Hello, Ajax5 world!</title>

  <script type="text/javascript"
    src="http://localhost:3000/static/js/jquery-1.2.1.js"></script>

  <script type="text/javascript">
  $(function() {
          // code à exécuter quand le DOM est chargé

          $("#toto").keyup(                       // Lors de l'entree de chiffre dans
                   function(){                    //  l'input 'toto'
                        toto = $("#toto").val();  // param cgi de l'input 'id=toto'
                        $.ajax({                  // création XMLHttpRequest

                            type: "POST",
                            dataType: "html",
                            url: "/getresulttoto",
                            data: "toto="+toto,
                            success: function(resultat){
                                           $("#result").html(resultat);
                                           $("#remplace").html(resultat);                                           
                                           $("#remplace").attr({ style: "background: red"});
                                     }
                        });
                        return false;
                  });
        });
  </script>

 </head>

<body>
        <p>Test Ajax - JQuery</p>
<br />

        entrer un nombre: <input id="toto" type="text"/><br>

        result: <input id="result" type="text" name="autre"/>

        <div id="remplace">En attente de l'appel Ajax</div>

</body>
</html>

Je ne sais pas pour vous mais moi je trouve la méthode JQuery simple d'emploi et très élégante.

6. Les sources du chapitre

Elles sont disponibles ICI .


Add a comment

Validator_logo
Catapulse v0.06
( 0.116666 s)