Oct/090
Gestion générique des appels Ajax
Le fonctionnement au niveau du navigateur est revu, et on pose les briques de base pour une gestion générique des événements Ajax au niveau de l’interface.
Pour cela, jQuery offre de nombreux points de rattachement lorsqu’une requête Ajax est déclenchée.
Notre but sera de programmer des comportements génériques de la façon la plus personnalisable et en essayant de rester au plus près du style de programmation jQuery.
Création de la vue
Des bibliothèques jQuery de messagerie informative et non intrusive existent, comme par exemple jGrowl. Nous prévoyons plutôt un système simple d’affichage, dans lequel un unique message est affiché à la fois. La référence ici est celle de google reader, à savoir un simple bandeau en position fixe en haut de l’écran.
Code HAML priv/templates/views/index.html.haml
:
%body #header #logo MyApp #global_msg #main ...
La feuille de style priv/templates/styles/application.css.sass
:
#global_msg :background-color #DDD :border-bottom 1px solid #555 :top 41px :display none :position fixed :left 0 :width 100% :padding 2px
Rien de particulier ici.
On lance une action rake front
pour transformer les modèles en fichiers cibles.
Création des comportements
Les fonctions javascript génériques seront écrites dans leur espace de nom. Pour cela, on « étend » jQuery afin de proposer les fonctions en tant que valeur de clés Json (la clé = le nom de la fonction / la valeur = la fonction anonymisée).
Pour cela, toutes les fonctions seront stockées dans le bloc suivant :
$.extend({ });
On commence par définir les éléments génériques suivants :
$.extend({ // Global messages, depending on locale messages: { forbidden: "You don't have access to this resource", server_error: "A problem occurred on the server", loading: "Loading...", loaded: "Data loaded" }, // Effect when a global message expire globalMessageTimeout: function() { $("#global_msg").slideUp("fast"); } });
Ensuite, on ajoute les méthodes de rappel qui seront associées à chaque événement Ajax :
$.extend({ ... globalAjaxStartEvent: function(event) { clearTimeout(this.timeout); this.timeoutDelay = 3000; $("#global_msg").text($.messages.loading).show(); },
Cette fonction sera appelée avant toute action Ajax. On en profite pour afficher la zone de message. De plus, on réinitialise la fonction utilisée lorsque le délai d’affichage est dépassé. On défini aussi la durée d’affichage par défaut du message. Les éléments de timeout
seront associés directement à l’objet sur lequel l’événement Ajax sera joué ; d’où le mot clé this
.
... globalAjaxEndEvent: function(event, request, ajaxOptions) { if (request.status == 200) { $("#global_msg").text($.messages.loaded); this.timeoutDelay = 500; } this.timeout = setTimeout($.globalMessageTimeout, this.timeoutDelay); },
Cette méthode est appelée à la fin d’un événement Ajax, quel que soit le résultat. Si l’action Ajax est correcte, on diminue le délai d’affichage, puis on déclenche la méthode qui permet d’enlever la zone de message de l’interface (à la fin du délai bien sûr).
... globalAjaxErrorEvent: function (event, request, ajaxOptions, thrownError) { msg = request.statusText; if (request.status == 403) { msg = $.messages.forbidden; } else if (request.status == 502) { msg = $.messages.server_error; } $("#global_msg").text(msg); } });
Cette fonction est appelée si une action Ajax s’est mal passée : ici on traduit un code d’erreur en message lisible par l’utilisateur. Et on l’affiche dans la zone de message.
Au final, on a cette structure priv/www/assets/scripts/app-core.js
:
$.extend({ messages: { ... }, globalMessageTimeout: function() { ... }, globalAjaxStartEvent: function(event) { ... }, globalAjaxEndEvent: function(event, request, ajaxOptions) { ... }, globalAjaxErrorEvent: function (event, request, ajaxOptions, thrownError) { ... } });
Affectation des évenements
La réaction aux événements doit être réalisée de façon globale à la page. On décide donc d’affecter les événements au tag
HTML le plus à la racine : le tag body
.
jQuery déclenche des événements au niveau de l’élément sur lequel on clique (en général, un lien). Ces événements sont propagés à travers les ancètres de l’élément dans l’arbre DOM. Et ceci jusqu’à ce qu’ils soient trappés ou arrivent à la racine (ce qui est nommé event delegation
). Ce fonctionnement permet par exemple de faire des événéments live qui fonctionnent correctement même lorsque des éléments sont ajoutés au DOM de façon dynamique (voir, par exemple, ce blog)
Pour notre part, on rajoute à notre fichier précédent ce type de code :
$(function() { $("body").bind("ajaxStart", $.globalAjaxStartEvent) .bind("ajaxComplete", $.globalAjaxEndEvent) .bind("ajaxError", $.globalAjaxErrorEvent) });
Le fait de coder de cette façon permet d’avoir une lisibilité accrue lors de l’utilisation des fonctions, ce qui est extrêmement important lorsqu’on sera obligé de reprendre ce javascript dans quelques mois (on sait tous que c’est inévitable).
Tous les évenements Ajax passeront alors par nos fonctions génériques. Toute propriété ajoutée (timeout
) sera stockée au niveau du body
.
Nous sommes alors parés pour continuer les développements en étant assurés d’avoir une base solide pour gérer les événements applicatifs.