Accueil > Java EE > Routes et templates Ember.js

Routes et templates Ember.js

Cet article est le troisième d’une série consacrée à 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.

Dans le précédent article, nous avons déjà écrit le routeur spécifiant les différents états de l’application. Rappel:

App.Router.map(function () {
    this.resource('catalog', function () {
        this.resource('category', { path: '/category/:category_id' }, function () {
            this.resource('item', { path: '/item/:item_id' });
        });
    });
    this.route('checkout');
});

Nous allons maintenant implémenter les routes et les templates permettant de naviguer dans l’application.
Récupérer la branche step1 du projet git si vous avez manqué le précédent article.

git checkout step1

En cas de problème vous pourrez regarder la version finale de ce 3ème article dans la branche step2.

Continuons… Une ressource est une route spécialisée regroupant d’autres routes.
Naviguer dans l’application revient donc à faire le rendu des différentes routes rencontrées sur le chemin.

Préparatifs

Mise en page dans le template application

Qui dit hiérarchie de route dit hiérarchie d’IHM. Nous devons donc d’abord éditer le template application pour indiquer à Ember via un helper {{outlet}} l’emplacement où il devra insérer le rendu d’une route enfant:

<script type="text/x-handlebars" data-template-name="application">
    <header>
        <h1><a href="#">Flashy clothes store</a></h1>
        <h4>An Ember.js demo application</h4>
    </header>
    {{outlet}}
</script>

S’outiller pour débugger en cas de problème

En cas de problème dans ce tuto, pas de panique, Javascript et Ember sont maintenant bien outillés.
1er réflexe: ouvrir la console du navigateur pour chercher l’ erreur et afficher sa stacktrace. Si celà ne suffit pas d’autres solutions existent:

Logger les transitions du routeur (en dev)
var App = Ember.Application.create({
    LOG_TRANSITIONS: true
});
Utiliser Chrome et installer l’extension Ember Inspector


Ouvrir les outils de développement et sélectionner l’onglet Ember pour découvrir l’outil:

Implémenter le catalogue du magasin

Une route Ember charge un modèle et effectue le rendu pour le template et le controleur qui lui sont associés par convention de nommage. Il est possible mais déconseillé de by-passer ces conventions – consulter la doc Ember si besoin.
Les routes et les controleurs sont des singletons qui sont instanciés par Ember au démarrage de l’application. Si une route ne trouve pas de template, elle ne fera pas de rendu. Si elle ne trouve pas de controleur, elle en instanciera un par défaut, qui servira de proxy vers le modèle.

La route catalog

Ajouter dans app.js une classe App.CatalogRoute et y implémenter la méthode model qui par convention sert de fabrique pour le modèle:

App.CatalogRoute = Ember.Route.extend({
    model: function () {
        return this.store.findAll('category');
    }
});

Note: Lors de l’initialisation de l’application, le module Ember Data s’intègre en demandant à Ember d’injecter le store dans les instances de route et de controleur.

Le controleur catalog

Rien à faire. Laissons Ember en instancier un par défaut: un ArrayController après avoir déterminé que le modèle cible est un tableau.

Le template catalog

Ajouter dans index.html un template catalog avec un menu permettant de naviguer vers les rayons du catalogue.

<script type="text/x-handlebars" data-template-name="catalog">
    <nav>
        {{#each category in controller}}
            {{#link-to "category" category}}
                {{category.name}}
            {{/link-to}}
        {{/each}}
    </nav>
    {{outlet}}
</script>

Le helper Handlebars {{#each category in controller}} itère sur les éléments du modèle servi par le controleur.

{{#link-to "category" category}} créé un lien html <a> pour naviguer vers la route category en passant en paramètre le modèle.

Ajouter maintenant un minimum de css et remplacer l’intitulé du rayon par une icone que l’on dimensionne.

<script type="text/x-handlebars" data-template-name="catalog">
    <nav class="categories">
        {{#each category in controller}}
            {{#link-to "category" category}}
                <i {{bind-attr class="category.cssClass"}} style="font-size: 64px;"></i>
            {{/link-to}}
        {{/each}}
    </nav>
    {{outlet}}
</script>

Le helper {{bindAttr class="category.cssClass"}} permet de binder l’attribut class de l’élément html sur la propriété cssClass du rayon category.

Remarques:
  • Naviguer vers la ressource catalog revient en fait à naviguer vers sa route implicite catalog.index.
  • L’application ne crashe pas si une route telle que catalog.index n’est pas implémentée car Ember instancie des route/controleur/template par défauts qui ne produisent rien.
  • En naviguant vers le détail d’un rayon, Ember identifie le lien html qui matche et lui assigne automatiquement la classe css active, ce qui nous permet de le styler pour le différencier.

Implémenter le rayon du magasin

La route category

Ne rien faire. Inutile d’implémenter la route App.CategoryRoute grâce au segment dynamique défini précédement pour la ressource.

App.Router.map(function () {
   ...
   this.resource('category', { path: '/category/:category_id' }, function () {
   ...
});

Ember sait par convention de nommage qu’il doit charger comme modèle un rayon (category) d’id donné en paramètre. Il instancie pour nous une route par défaut qui serait équivalente à celle-ci:

App.CategoryRoute = Ember.Route.extend({
    model: function (params) {
        return this.store.find('category', params.category_id);
    }
});

Le controleur category

Ne rien faire. Ember.js instancie pour nous un controleur par défaut ObjectController (le modèle est un objet unique).

Créer le template category

Itérer sur les produits du rayon servant de modèle pour les afficher sous forme de liste (pour celà Ember Data charge à la demande la liste de produits en exécutant une requête ajax).

<script type="text/x-handlebars" data-template-name="category">
    <ul>
        {{#each item in items}}
            <li>
                {{#link-to "item" item}}
                    <i {{bind-attr class="item.cssClass"}} style="font-size:160px;"></i>
                    $ {{item.price}}
                {{/link-to}}
            </li>
        {{/each}}
    </ul>
    {{outlet}}
</script>

Ajouter maintenant un minimum de css en jouant avec les Block Grid de Foundation, et affichant le prix dans un label Foundation dans le coin haut-droit.

<script type="text/x-handlebars" data-template-name="category">
    <ul class="small-block-grid-2 medium-block-grid-3 large-block-grid-4 items">
        {{#each item in items}}
            <li>
                {{#link-to "item" item}}
                    <i {{bind-attr class="item.cssClass"}} style="font-size:160px;"></i>
                    <span class="radius label item-price">$ {{item.price}}</span>
                {{/link-to}}
            </li>
        {{/each}}
    </ul>
    {{outlet}}
</script>

Petite aparté sur Foundation:

  • small-block-grid-2: grille responsive de 2 éléments par ligne sur les petits écrans (smartphone).
  • medium-block-grid-x et large-block-grid-x correspondent respectivement aux moyens et grands écrans.

Amusez-vous à redimensionner la fenêtre du navigateur pour observer le changement de mise en page dynamique.

Implémenter le détail d’un produit

La route item

Ne rien faire. Inutile d’implémenter la route App.ItemRoute, Ember en génère une par défaut qui charge le modèle grâce à la définition du segment dynamique /item/:item_id.

Le controleur item

Ne rien faire. Ember.js pour nous en instancie un par défaut (ObjectController).

Créer le template item

Dans le nouveau template, afficher l’illustration graphique et les informations du produit servant de modèle:

<script type="text/x-handlebars" data-template-name="item">
    <i {{bind-attr class="cssClass"}} style="font-size:300px;"></i>
    <h3>{{name}}</h3>
    <h4>${{price}}</h4>
    <p>{{description}}</p>
</script>

Bonus: utiliser la grille responsive de Foundation pour adapter la mise en page à la taille de l’écran (1 colonne sur petit, 2 colonnes sur moyen ou grand).

<script type="text/x-handlebars" data-template-name="item">
    <div class="row">
        <div class="small-12 medium-6 columns">
            <i {{bind-attr class="cssClass"}} style="font-size:300px;"></i>
        </div>
        <div class="small-12 medium-6 columns">
            <h3>{{name}}</h3>
            <h4>${{price}}</h4>
            <p>{{description}}</p>
        </div>
    </div>
</script>

Il est maintenant possible de parcourir les rayons de notre site de commerce en ligne et d’afficher le détail d’un produit en bas de page.
Amusons nous aussi à modifier les paramètres d’url, à poser un signet ou à jouer avec l’historique navigateur (back ou forward). Ember écoute les changements d’url, pour désérialiser, reconstituer l’état de l’application (routes + modèles) et faire le rendu du delta.

Prochainement…

Parce-que le détail d’un produit manque de visibilité en bas de page et que ce serait plus joli dans une modale, nous apprendrons dans un prochain article à manipuler le dom grâce aux vues Ember.

Categories: Java EE Tags: , , , , ,


+ 6 = quinze