Faire du MVVM en JavaScript avec Knockout

MVVM est un pattern de développement signifiant Model-View-ViewModel. Ce pattern est une adaptation du pattern MVC afin de tirer parti du binding multi-directionnel apporté par des technologies comme WPF ou Silverlight. La vue se « binde » sur des objets Model exposé par le ViewModel. L’avantage de ce pattern est que la vue se met à jour automatiquement si l’objet sur lequel elle se binde change.

J’ai mis en place ce pattern sur un de mes projets en WPF et la facilité d’utilisation de ce pattern m’a poussé à rechercher s’il existait quelque chose d’équivalent en JavaScript.

La réponse à cette question est Knockout.

Voici une présentation rapide des principaux concepts et mécanisme de cette librairie.

Mise en place

Il suffit de télécharger la librairie et de l’inclure dans votre page web :

<script type='text/javascript' src='knockout-2.0.0.js'></script>

Utilisation

Création des vue modèles

La première chose à faire est de créer le Model et le vue ViewModel.

Pour notre exemple, nous allons créer un objet Model Person qui contient 2 propriétés : un nom (name) et un age (age).

var Person = function(name, age) {
    var self = this;
    self.name = name;
    self.age = age;
}

Nous créons ensuite un objet qui va être notre ViewModel qui va exposer un objet Person.

var MyViewModel = function() {
     //Utilisation de self pour éviter les erreurs avec this
     var self = this;
     self.me = ko.observable(new Person('Julien', 30));
 }

On peut voir ici l’utilisation de ko.observable qui permet de s’abonner au changement survenant sur la propriété me du ViewModel. Ainsi, ma vue qui viendra se « binder » sur mon ViewModel sera mise à jour automatiquement si ma propriété me venait à changer. Si on ne souhaite pas être notifier des modifications, on définit simplement notre objet Person.

On peut définir ainsi des objets simples mais également des collections (objets Array ou via ko.observableArray), que nous verrons un peu plus loin mais également des fonctions qui permettrons d’interagir avec nos données exposées.

var MyViewModel = function() {
     //Déclaration des propriétés
     ....

     self.helloWorld = function() {
         alert('Hello World from KnockOut');
     }
 }

Binding

Il faut ensuite binder nos éléments HTML sur les données exposées par noter ViewModel. Pour cela, Knockout utilise l’attribut data-* qui permet de rajouter des informations personnalisé sur des éléments sans perdre la validité du document. Ainsi, Knockout permet de binder via l’attribut … data-bind.

<input type="text" data-bind="value : me().name" />
<span data-bind="text : me().age" </span>

On va pouvoir ainsi « binder » un textbox, un label, un bouton … sur les données ou une méthode du ViewModel.

<input type="text" data-bind="maVar" />
<input type="button" data-bind="click : startGame" />

Afin que le binding se passe, il faut « lancer » knockout via la méthode applyBindings avec en paramètre le ViewModel.

//On applique les bindings
ko.applyBindings(new MyViewModel());

Knockout met à disposition plusieurs binding qui vont permettre d’interagir avec les éléments (afficher (visible), activer (enable), clic (click), …)

Array et Template

Comme évoqué plus haut, Knockout permet également la possibilité de gérer des collections. Dans le cas où l’on veut gérer les modifications et en être notifié, on utilise un observableArray sinon un Array classique JavaScript.

Cette notion de collection est couplée à la notion de template qui va nous permettre de définir un modèle qui sera utilisé pour le rendu de chaque élément de la liste.

On peut définir un template de 2 façons : de façon anonyme ou entre 2 balises script de type text/html.

Au final, voici le code html :

<h2>Participants</h2>
Liste des participants :

<table>
    <thead>
    	<tr>
		<th></th>
		<th>Name</th>
		<th>Age</th>
	</tr>
    </thead>
    <!-- Template Anonyme -->
    <tbody data-bind="foreach: persons">
    	<tr>
		<td><a href='#' data-bind='click: $root.removePerson'>Delete</a></td>
        	<td data-bind="text: name"></td>
        	<td data-bind="text: age"></td>           
        </tr>
    </tbody>
</table>

<!--Utilisation d'un template -->
<div data-bind="template: { name: 'person-template', foreach: persons}"></div>
 
<!-- Déclaration du template -->
<script type="text/html" id="person-template">
    <h3 data-bind="text: name"></h3>
    <p>Age : <span data-bind="text: age"></span></p>
</script>

<!-- Formulaire d'ajout -->
<h4>Ajouter un participant</h4>
<form>
    <label for="txtName">Nom</label> <input type="text" id="txtName" data-bind="value : newPerson().name" />
    <label for="txtAge">Age</label> <input type="text" id="txtAge" data-bind="value : newPerson().age" />
    <button data-bind="click:addPerson">Add</button>
</form>

Notre VueModel :

//initialisation du View Model
 var MyViewModel = function() {
     var self = this;

     self.persons = ko.observableArray([new Person('Franklin', 25), new Person('Mario', 58) ]);

     /*** Functions ***/
     self.removePerson = function(person) {
          self.persons.remove(person);
     }
				
     self.addPerson = function() {
          self.persons.push(self.newPerson());
          self.newPerson(new Person('', 0));					
     }
}
//On applique les bindings
ko.applyBindings(new MyViewModel());

Le lien vers la documentation concernant les templates de KnockOut. On peut notamment voir comment utiliser le moteur de template
de JQuery (jquery.tmpl), changer de template dynamiquement, …

Voici le lien vers la démo décrite dans cet article ainsi qu’une petite application permettant le gérer le score d’un match de Volley Ball (Cette application gère le offline, et vous demandera, si votre navigateur le supporte, le stockage de donnée pour une utilisation hors connexion. Je reviendrai dans un prochain article sur le offline).

Liens

Conclusion

KnockOut est une librairie JavaScript puissante qui permet de faire du MVVM et de bénéficier de la puissance du binding (le debuggage n’est pas forcément aisé, et on perd rapidement un peu de temps au début …). Cette librairie peut être couplée avec d’autres librairies comme JQuery par exemple pour encore plus de puissance …
Je pense que ce genre de librairies va se développer afin de permettre un développement plus structuré des applications HTML5 qui sont promise à un bel avenir quelque soit le support (Mobile, Web, Windows avec Windows 8) …

D’autres librairies comme BackoneJS ou EmberJS sont également très à la mode en ce moment, implémentant le pattern MVC.

Pour information, Microsoft a publié le code source d’ASP.NET MVC sous une licence open source. Celui-ci intègre Knockout.js (de même que plusieurs autres technologies open source comme jQuery, jQuery UI, jQuery Mobile, jQuery Validation, Modernizr.js, NuGet, et JSON.NET).

Une réflexion au sujet de « Faire du MVVM en JavaScript avec Knockout »

  1. Excellent billet. Et je trouve cette commande très pertinente. Pour moi, j’ai bénéficié d’un tutoriel sur http://www.alphorm.com pour maîtriser Knockout et de faire du pattern MVVM, le ViewModel et tant d’autre encore aussi. En tout cas merci pour ce partage.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *