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
- knockmeout.net : le blog du développeur de KnockOut
- Documentation de Knockout
- les tutoriaux / exemples très bien faits et interactifs
- KnockoutJS context debugger for Chrome : Une extension Chrome pour avoir des informations concernant vos objet knockout dans la barre d’outils développeurs de Chrome
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).
Tweet
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.