Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Structurer son code jQuery avec Backbone.js

Compatible JavaScript. Cliquer pour en savoir plus sur les compatibilités.Par dcz.switcher (david chollez), le 05 février 2012

jQuery est une librairie géniale, mais en aucun cas un framework pour le développement.

Or, pour le développement d'applications - en particulier -, il est nécessaire de disposer d'une structure dans le code permettant une maintenance et une évolution future aisée.

Adopter une approche MVC est alors une bonne option et c'est ce que permet de faire Backbone.js

Prérequis Pour suivre ce tuto, vous devez avoir quelques connaissance en HTML, javascript et avoir déjà utilisé la librairie jQuery.

Pour tester notre code, il est également nécessaire d'utiliser la console javascript intégrée au navigateur.

Sous Google Chrome : Afficher / Options pour les développeurs / Console javascript

Backbone.js permet d'adopter une approche MVC, où l'on sépare l'affichage, l'accès aux données et ce qui fait le lien entre les 2.

La séparation permet de faire évoluer l'application sans devoir tout ré-écrire : un changement d'interface, une autre manière de récupérer les données dans la base etc.

http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur

De plus, Backbone propose par défaut une approche REST qui permet de simplifier les échanges avec le serveur sur les opérations de : création, modification et suppression.

http://fr.wikipedia.org/wiki/Representational_state_transfer

Backbone propose donc plusieurs “classes” dont voici les 3 principales :

View : ce sont les données affichées dans la page

Model : un objet chargé et manipulé, un utilisateur, un article dans un panier etc.

Collection : un ensemble de Model

Nous allons mettre en oeuvre ces 3 classes au travers d'un exemple simple : l'affichage d'une liste d'utilisateurs.

Préparation

Pour commencer, on récupère Backbone.js et underscore.js (et bien sûr jQuery ^^ ) http://backbonejs.org/ http://underscorejs.org/

Les répertoires de notre exemple seront organisés comme suit:

. index.html
| js
   | libs
       . backbone-min.js
       . jquery.min.js
       . underscore-min.js
   | views
       . index.js

Dans la page index.html, on charge les scripts.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>tuto mediabox</title>
</head>
<body>
 
<!--
  on placera notre code HTML ici
-->
 
<!-- SCRIPTS -->
	<script type="text/javascript" src="js/libs/jquery.min.js"></script>
	<script type="text/javascript" src="js/libs/underscore-min.js"></script>
	<script type="text/javascript" src="js/libs/backbone-min.js"></script>
 
	<script type="text/javascript" src="js/views/index.js"></script>
</body>
</html>

On se place ensuite dans notre index.js

Le Model

Nous allons définir un Model qui se nomme User et qui correspond à un utilisateur. Par défaut, les utilisateurs ont tous le rôle “invite”, on le précise dans notre model Enfin, on instantie notre user

//model User
var User = Backbone.Model.extend({
    defaults : {
        "role" : "invité"
    }
}) ;
 
var user = new User({ id : 1, nom : 'david'}) ;
console.log(user) ;

Dans la console javascript, une fois la page index.html affichée, on peut voir notre objet Model

attributes: Object
    id   : 1	
    nom  : "david"	
    role : "invité"

Bien entendu, BB propose plusieurs fonctions pour manipuler les Models

user.set({'age': 37} ) ; //attribuer la valeur 37 à l'attribut age (et créer ce dernier s'il n'existe pas)
user.get('age') ; //retourne la valeur de l'age

La Collection

La collection est un ensemble de modèle.

//création d'une collection du model User
var UserCollection = Backbone.Collection.extend({
   model : User
}) ;

Dans le cadre de notre exercice, les données de la collection seront en dur.

Il y a plusieurs méthodes pour charger une collection, en voici une.

    var dataColl = [
        {id : 1, nom : "bob", age : 10},
        {id : 2, nom : "patrick", age : 10},
        {id : 3, nom : "carlo", age : 10}
    ];
 
    var userColl = new UserCollection(dataColl) ;
 
    console.log(userColl.models) ;

En rafraichissant la page, on voit dans la console javascript les models de notre Collection …

Il est maintenant aisé de manipuler les Models dans notre Collection

userColl.get(1).get('nom') //retourne le nom de l’utilisateur 1
userColl.length  ; //retourne le nombre d'éléments dans la Collection
userColl.remove( userColl.get(1) ) ; //supprime celui qui porte l'id 1…

La View

On va à présent afficher la liste dans notre page HTML

Note : Il y a plusieurs manières de faire, voici une méthode qui considère que la vue est la liste, mais on peut également avoir une vue pour la liste ainsi qu'une vue par élément de cette liste. En fait, tout dépend du contexte, il n'y a pas de bonne solution unique.

Pour commencer, dans la page index.html, on crée notre liste qui porte l’id “user-ul”

<body>
    <div class="content">
        <h1>Tutoriel Mediabox - backboneJS</h1>
        <div>
	    <ul id="user-ul"></ul>			
	</div>
    </div>
...

Dans le fichier index.js, on crée ensuite notre vue

var UserView = Backbone.View.extend({
    el : '#user-ul',
 
    initialize : function () {
	console.log('vue créée');
        console.log(this.$el);
    }
});
 
// instantiation de la vue
var userView = new UserView();

Dans notre vue, on défini l'élément HTML de cette dernière avec l'attribut “el”

L'objet View appelle systématiquement la méthode “initialize()”, dans laquelle nous allons pouvoir charger la liste

Enfin, on instantie la vue

Reste à présent à mettre en musique la vue, le model et la collection

Mais avant cela, un petit aparté sur les templates

Pour le rendu de notre liste, on peut simplement créer une chaine de caractère HTML en itérant dans les objets de la collection, on aura quelque chose dans ce goût là

var htmlString = '',
    user       = {};
for (var i = 0, ii = collection.length; i < ii ; i++) {
    user = collection.get(i);
    htmlString += '<li>' + user.get('nom') + </li>';
}
 
$('#user-ul').html( htmlString );

Le problème de cette méthode est qu'on utilise des balises HTML dans le code JS, si on décide d'afficher les utilisateurs dans une table au lieu d'un liste, il faudra alors modifier le code javascript.

Pour éviter cela, on va utiliser un template et ça tombe bien, underscroreJS propose sont propre système !

http://underscorejs.org/#template

La première chose à faire est donc de créer son template dans la page HTML (on peut externaliser les templates, mais ça n'est pas l'objet de ce tutoriel

Voici comment définir une template underscoreJS

<script type="text/template" id="user-row-tpl">
    <li data-id="<%= id %>"><%= nom %> ( <%= role %> )</li>
</script>

Il suffit ensuite de passer à ce template un objet contenant les attributs id, nom et role pour le charger

Vous comprenez maintenant mieux la souplesse si on devait charger les users dans une table.

<script type="text/template" id="user-row-tpl">
<tr>
    <td data-id="<%= id %>"><%= nom %> ( <%= role %> )</td>
</tr>
</script>

Pour finir, on utilise le template dans notre vue

J'ai essayé de commenter au mieux le code ci-dessous

var UserView = Backbone.View.extend({
    el : '#user-ul',
 
    initialize : function () {
        // instantiation de la collection en lui passant le tableau des users
	this.coll     = new UserCollection(dataColl);
 
        // référence au template 
	this.template = _.template($('#user-row-tpl').html());
 
        appelle le rendu de la vue pour afficher la liste
	this.render();
    }, 
 
    render : function () {
	var html = '',
            that = this;
 
        // itération dans la collection et création du HTML
	this.coll.each(function (user) {
	    html += that.template(user.attributes);
	});
 
        // on insère le HTML dans l'élément HTML qui constitue notre vue
	this.$el.html(html);
    }
});

Si tout ce passe bien, vous devriez avoir dans votre page HTML la liste des users !

Si non, voici le code complet

La page HTML

<body>
    <div class="content">
         <h1>Tutoriel Mediabox - backboneJS</h1>
	<div>
    	    <ul id="user-ul"></ul>			
	</div>
    </div>
 
<!-- TEMPLATES -->
	<script type="text/template" id="user-row-tpl">
		<li data-id="<%= id %>"><%= nom %> ( <%= role %> )</li>
	</script>
 
<!-- SCRIPTS -->
	<script type="text/javascript" src="js/libs/jquery.min.js"></script>
	<script type="text/javascript" src="js/libs/underscore-min.js"></script>
	<script type="text/javascript" src="js/libs/backbone-min.js"></script>
 
	<script type="text/javascript" src="js/views/index.js"></script>
</body>

Le script index.js

(function ($) {
 
    // bootstrap data
    var dataColl = [
        {id : 1, nom : "bob", age : 10},
        {id : 2, nom : "patrick", age : 10},
        {id : 3, nom : "carlo", age : 10}
    ];
 
    // le model
    var User = Backbone.Model.extend({
	defaults : {
		"role" : "invité"
	}
    }) ;
 
    // la collection
    var UserCollection = Backbone.Collection.extend({
	model : User
    }) ;
 
    // la vue
    var UserView = Backbone.View.extend({
	el : '#user-ul',
 
	initialize : function () {
    	    this.coll     = new UserCollection(dataColl);
	    this.template = _.template($('#user-row-tpl').html());
	    this.render();
	},
 
	render : function () {
	    var html = '',
		that = this;
 
	    this.coll.each(function (user) {
		html += that.template(user.attributes);
	    });
 
	    this.$el.html(html);
	}
    });
 
    var userView = new UserView();
 
})(jQuery);

les évènements

Pour mettre un peu d'interactivité, nous allons écouter les click sur les éléments de la vue.

Le click sur un user affichera sont id dans une alert()

Pour placer des écouteurs dans notre View, on utilise l'attribut “events” :

Voici la vue modifiée

var UserView = Backbone.View.extend({
    el : '#user-ul',
 
    initialize : function () {
	this.coll     = new UserCollection(dataColl);
	this.template = _.template($('#user-row-tpl').html());
	this.render();
    },
 
    events : {
	'click li' : 'clickHandler'
    },
 
    clickHandler : function (e) {
	alert($(e.currentTarget).data('id'));
    },
 
    render : function () {
	var html = '',
   	    that = this;
 
	this.coll.each(function (user) {
	    html += that.template(user.attributes);
	});
 
	this.$el.html(html);
    }
});

Conclusion

Et voilà, nous avons à présent un code clair et séparé qui nous permet une grande souplesse

Dans le prochain tutoriel, nous verrons la connexion à un serveur pour ajout, modification et suppression des users