LocalStorage, la suite logique de Google Gears ?
Vous connaissez bien sûr Gears, le plugin de Google vous permettant de stocker de faire persister facilement des informations côté client accessible par une API Javascript. La principale application du système est de permettre un accès à l’information en mode déconnecté ou bien comme un mécanisme de cache pour votre application web.
Or, il existe aujourd’hui, incorporé directement dans Firefox 3.5, les versions les plus récentes de Webkit (Safari 4 notamment) et, cerise sur le gâteau, IE8, un moyen similaire de stocker des informations. Il s’agit de l’objet window.localStorage sur lequel on peut directement travailler pour faire persister des données : ce qu’on y stocke est accessible pour tout le domaine, même après fermeture du navigateur. Il existe également une version basée sur la session courante qui s’appelle assez logiquement window.sessionStorage.
Décidé à expérimenter le système par moi-même, j’ai donc décidé de créer une todo-list très simple sous forme d’un plugin pour jQuery. L’intérêt pratique est bien sûr relativement limité car les données ne sont pas sauvegardées sur un serveur et ne sont donc accessibles et persistantes que depuis un seul navigateur sur un unique poste.
Cela m’a cependant permis de me familiariser avec la création de plugin pour jQuery et la réalisation de la todo-list elle-même était amusante. Pour les opérations de manipulation de chaînes JSON, j’ai choisi de me reposer sur la librairie jquery-json plutôt que de réinventer la lune à ce niveau.
Pour les plus impatients, voilà le lien pour voir la démo … Attention à bien utiliser un navigateur compatible
!
Commençons par le plus évident, la page HTML qui va nous servir de base :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Une todo-list avec localStorage</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="http://jquery-json.googlecode.com/files/jquery.json-2.2.min.js"></script>
<script type="text/javascript" src="js/todolist.class.js"></script>
<script type="text/javascript" src="js/todolist.jquery.js"></script>
<link rel="Stylesheet" href="css/styles.css" />
<script type="text/javascript">
$(document).ready(function() {
$('#conteneur-todolist').todoList();
});
</script>
</head>
<body>
<h1>Une todo-list avec localStorage</h1>
<div id="conteneur-todolist"></div>
</body>
</html>
J’ai ensuite choisi de créer deux fichiers distincts qui vont me permettre de bien séparer la couche métier de mon implémentation.
Voilà tout d’abord la couche métier, la classe TodoList elle-même :
/**
La classe Todolist va nous permettre de manipuler les tâches et des les
faire persister dans la mémoire du navigateur via window.localStorage.
@param string identifiant
L'identifiant unique de la todoList
@param function cb_mise_a_jour
Fonction de callback qui sera appelée à chaque mise à jour de la todoList.
*/
var TodoList = function(identifiant, cb_mise_a_jour) {
// Cet identifiant sera sa clé dans le tableau du localStorage
this.identifiant = identifiant;
// On récupère les données qui existaient déjà,
// ou on utilise les données par défaut.
this.donnees = $.evalJSON(window.localStorage.getItem(this.identifiant)) || {
taches : [
{
'libelle': "Libellé de la première tâche",
'effectue': false
},
{
'libelle': "Libellé de la deuxième tâche",
'effectue': false
},
{
'libelle': "Libellé de la troisième tâche ... Elle est finie",
'effectue': true
}
]
};
// L'utilisateur devrait implémenter la méthode de callback pour
// rafraîchir son interface à chaque modification de la todolist.
this.cb_mise_a_jour = cb_mise_a_jour || function() {
throw 'Vous devriez implémenter la méthode cb_mise_a_jour(instance)';
}
// On exécute une première fois la méthode pour initialiser l'interface.
this.cb_mise_a_jour(this);
}
/**
Retourner toutes les tâches
*/
TodoList.prototype.lister_taches = function() {
return this.donnees.taches;
}
/**
Ajouter une nouvelle tâche
@param string libelle_tache Libellé de la tâche a ajouter.
*/
TodoList.prototype.ajout_tache = function(libelle_tache) {
this.donnees.taches.push({
'libelle': libelle_tache,
'effectue': false
});
this.sauvegarder();
}
/**
Modifier une tâche
@param int numero Numéro de la tâche à modifier.
@param object infos_nouvelle_tache La tâche après mise à jour
*/
TodoList.prototype.modification_tache = function(numero, infos_nouvelle_tache) {
jQuery.extend(this.donnees.taches[numero], infos_nouvelle_tache);
this.sauvegarder();
}
/**
Supprimer une tâche
@param int numero Numéro de la tâche à supprimer.
*/
TodoList.prototype.suppression_tache = function(numero) {
this.donnees.taches.splice(numero, 1);
this.sauvegarder();
}
/**
Faire persister la TodoList dans le localStorage
*/
TodoList.prototype.sauvegarder = function() {
window.localStorage.setItem(this.identifiant, $.toJSON(this.donnees));
this.cb_mise_a_jour(this);
}
Voilà maintenant le plugin jQuery à proprement parler :
/**
TodoList - Plugin pour jQuery
Utilisation : $(selecteur_div).todoList();
*/
(function($){
$.fn.todoList = function(options) {
// On peut utiliser plusieurs TodoLists sur la page
return this.each(function() {
// Définition des éléments de l'interface
var liste_taches = $('<ul></ul>');
var champ_texte = $('<input type="text" />');
var bouton_ajout = $('<input type="button" value="Ajouter" />');
$(this).append(liste_taches, champ_texte, bouton_ajout);
$(this).attr('class','todolist');
// Action permettant d'ajouter une nouvelle tâche à accomplir
var ajout_tache = function() {
var contenu_texte = $(champ_texte).val();
if(contenu_texte != '') {
// On crée une nouvelle entrée dans le tableau des tâches.
TDL.ajout_tache(contenu_texte);
}
}
// Ajout de l'écouteur du bouton.
$(bouton_ajout).click(ajout_tache);
// Ajout de l'écouteur du champ textuel
$(champ_texte).keyup(function(e) {
if(e.keyCode == 13) ajout_tache();
});
// Implémentation de la méthode de callback de la TodoList
var cb_mise_a_jour = function(TDL) {
// On vide la liste des tâches et le champ texte
$(liste_taches).html('');
$(champ_texte).val('');
// Pour chaque tâche ...
$.each(TDL.lister_taches(), function(i,tache) {
// On génère un identifiant pour chaque tâche
tache.identifiant = TDL.identifiant + '_tache_' + i;
// On ajoute la tâche à la liste
$(liste_taches).append(
$('<li></li>')
.attr('class',(tache.effectue)?'tache_effectuee':'')
.append(
$('<input type="checkbox" />')
.attr('id', tache.identifiant)
.attr('checked', tache.effectue)
.click(function() {
// Un clic sur la checkbox permet de changer le statut
// de la tâche visée.
TDL.modification_tache(i, {
'effectue': $(this).attr('checked')
});
})
,
$('<label></label>')
.attr('for', tache.identifiant)
.html(tache.libelle)
,
' '
,
// On ajoute la possibilité de supprimer la tâche
$('<a></a>')
.attr('href', 'javascript:void(0)')
.attr('title', 'Cliquez pour supprimer la tâche')
.click(function() {
$(this).parent().fadeOut(function() {
TDL.suppression_tache(i);
});
})
.html('x')
)
);
});
};
// Instanciation de l'objet TodoList
try {
var TDL = new TodoList('todolist_'+$(this).attr('id'), cb_mise_a_jour);
}
catch(e) {
// En cas d'erreur on avertit l'utilisateur.
alert("Une erreur s'est produite, il est probable que votre navigateur"+
" ne supporte pas encore la propriété window.localStorage \n\n" +
"Message retourné : \n" + e);
}
});
};
})(jQuery);
Et voilà, quelques styles CSS et on est au bout. En conclusion, je dirais que ce système est assez intéressant surtout pour servir éventuellement de fourre-tout, si votre application a une masse importante de données relativement statiques à transmettre au client. On pourrait imaginer dériver de cette TodoList pour créer un plugin généraliste pour jQuery qui permettait d’utiliser le localStorage s’il est disponible – et sinon se rabattre sur une simple variable globale … J’y réfléchis en ce moment, cela fera sans doute l’objet d’un post ultérieur
.
Mots clés : api, Développement, firefox, javascript, jquery, plugin
Articles liés :
