Accueil > Java EE > A la découverte d’Ember.js

A la découverte d’Ember.js

31/01/2014

Cet article est le premier d’une série consacré à la découverte d’Ember.js,  framework javascript pour créer des applications web single page, que j’utilise quotidiennement avec bonheur depuis maintenant plus d’un an.

Ember.js se démarque en imposant une architecture et des conventions permettant de structurer un projet et de gagner en productivité.
–   : le coût d’apprentissage est un peu plus élevé que d’autres frameworks (malgré les récents efforts de documentation et de stabilisation de l’api).
–   : 67kb minifié-compressé, sans compter les dépendances js obligatoires Handlebars et JQuery.
+ : en privilégiant les conventions à la configuration, le framework nous apporte beaucoup de magie pour plus de productivité.
+ : en utilisant la même recette imposée, tous les projets se ressemblent.

Concepts de base d’Ember.js

Modèle objet riche

Javascript est un langage orienté objet à prototype. Ember.js introduit la notion de classe afin de faciliter l’héritage.

Exemple de définition d’une classe utilisateur et de son instanciation:

App.User = Ember.Object.extend({
   //propriété avec une valeur par défaut
   creationDate: new Date()
});
var cowboy = App.User.create({
   firstName: 'Clint',
   lastName: 'Eastwood'
});

Tout objet Ember se doit d’hériter, même indirectement, de la classe de base Ember.Object pour bénéficier de fonctionnalités minimales:

  • Propriétés calculées

Exemple d’une propriété fullName calculée comme étant la concaténation des propriétés firstName et lastName:

App.User = Ember.Object.extend({
   fullName: function() {
      return this.get('firstName') + ' ' + this.get('lastName');
   }.property('firstName','lastName')
});

La valeur est gardée en cache et n’est recalculée que si l’une des 2 dépendances change.

  • Observateurs

Exemple d’un observateur alertant l’utilisateur en cas de changement d’age:

App.User = Ember.Object.extend({
   ageChanged: function() {
      alert('age changed');
   }.observes('age')
});
  • Binding

Exemple de binding des revenus du mari sur ceux de sa femme:

App.wife = Ember.Object.create({
   householdIncome: 80000
});
App.husband = Ember.Object.create({
   householdIncomeBinding: 'App.wife.householdIncome'
});

Par convention, une propriété bindée doit être suffixée par Binding et se voir assigner une expression.

  • Getter/Setter

L’usage de getters/setters est verbeux mais nécessaire pour, par exemple, accéder à une propriété calculée ou modifier une valeur tout en déclenchant les notifications.
Utilisons les pour illustrer l’exemple de binding précédent:

App.husband.get('householdIncome'); //obtient 80000
App.wife.set('householdIncome', 100000);
App.husband.get('householdIncome'); //obtient 100000

Routeur

Ember nous oblige à modéliser les états de l’application en définissant un routeur dont la hiérarchie des routes doit matcher celle des IHMs. Il suffit alors au routeur de traduire une URL en une séquence de templates imbriqués pour effectuer le rendu.

Exemple pour l’url index.html#/catalog/category/1/item/2 d’un site de vente de vêtements:

  • Application(racine): vue titre + panier
    • Catalog: vue liste des rayons (costumes, tshirts, robes,…)
      • Category: vue liste des vêtements du rayon sélectionné
        • Item: vue détail (dans une popup) du vêtement sélectionné

Comme nous le verrons plus tard, chaque route se voit associée par convention de nommage au template Handlebars et au controlleur nécessaires au rendu.

La route est aussi responsable de la construction du modèle et de sa sérialisation dans l’URL. Dans notre exemple d’URL précédent, l’identifiant de catégorie 1 permet à la route category de rechercher le modèle necessaire lors de l’ouverture d’un signet ou d’un back navigateur.

Template Handlebars

Un template Handlebars est un script contenant du code html et des expressions entre doubles accolades permettant d’ajouter du comportement et de dynamiser le rendu.

Exemple d’un template d’affichage d’une liste d’utilisateurs:

<html>
<body>
...
<script type="text/javascript" data-template-name="users">
<ul>
   {{#each user in users}}
	<li>{{user.fullName}}</li>
   {{/each}}
</ul>
</script>
...
</body>
</html>
Expression Description
{{user.fullName}} Affiche une propriété
{{#with user}}{{fullName}}{{/with}} Change de contexte
{{#if user}}{{user.fullName}}{{else}}Aucun utilisateur{{/if}} Affichage conditionnel du bloc (else facultatif)
{{#unless user}}Aucun utilisateur{{/unless}} Affichage conditionnel similaire
{{#each user in users}}{{user.fullName}}{{/each}} Itération sur la liste d’objets users
{{#link-to "user" user}}Afficher{{/link-to}} Créé un lien html vers la route user en passant comme modèle une instance user
<button {{action show}}>Afficher</button> Déclenche l’action implémentée dans la méthode show du controlleur
<div {{bindAttr class='visible'}}> Bind l’attribut d’un élément html

Les templates sont compilés par Ember, ici au runtime, sous forme de fonctions js produisant du code html.
Il est fortement conseillé sur un vrai projet de pré-compiler les templates lors du build pour accélérer le temps d’initialisation de l’application.

Controlleur

Un controlleur Ember est un proxy destiné à servir le modèle au template.
Il peut aussi posséder des propriétés et fonctions permettant de gérer l’état de l’application.
Nous utilisons soit un ObjectController pour un modèle unique, soit un ArrayController pour une liste.

Règle de résolution d’une expression Handlebars référençant une propriété ({{name}} par exemple): référence une propriété du controlleur si elle existe, une propriété du modèle proxifié le cas échéant.

Prochainement…

Les concepts fondamentaux acquis, passons à la pratique en construisant notre première application Ember (prochain article).

Categories: Java EE Tags: , , , , ,
Les commentaires sont fermés.