Ember.js ,Think different ! (Etape 1)

Le premier article nous a parlé théorie. Passons maintenant à la pratique avec une première application qui s’étoffera progressivement au fil des articles.

Cet article fait partie d’une série composée des posts suivants :

  • Introduction à Ember – Rappel rapide sur les mécanismes mis en place par ce framework,
  • Etape 1 (ce post) – Application simple avec données ‘rack’ retournées directement par le modèle sous forme d’un tableau JSON. Cette étape sera complété par l’utilisation de addons Ember pour rendre l’application plus esthétique et ergonomique.
  • Etape 2 –  Passage sur un modèle géré par Ember Data. Etant donné qu’aucun back-office ne sera déployé, nous introduirons le framework de mock Mirage pour intercepter les appels HTTP.
  • Etape 3 – Introduction d’une relation rack —> bottle, et enrichissement des données mockées.
  • Etape 4 –  Construction d’un composant graphique permettant une représentation d’un rack et simplifiant les templates d’affichage
  • Etape 5 – Mise en place d’un backoffice construit à l’aide de JHipster. Nous profiterons des capacités de cet outil pour obtenir rapidement un modèle persisté en base, les services Rest permettant de l’administrer ainsi qu’une interface d’administration. Nous verrons au passage quelques problématiques liées à la sécurité.
  • Etape 6 – Mise en place d’un backoffice sur services Liferay et packaging de l’application Ember au sein d’une portlet. Nous avons choisi un backoffice Liferay pour sa facilité à construire un modèle de persistance mais surtout pour bien démontrer l’utilisation potentielle des Adapters et des Serializers, même si un backoffice plus conforme aux standards Rest n’en sera que plus aisé à utiliser.

Le code de cet article est disponible sur mon gitlab dans les branches step_1_x.

Step 1.1 – Mise en place du projet

La création du projet ‘Ember Cellar’ passe par la commande :

ember new EmberCellar

Cette commande crée un répertoire contenant la structure de fichiers suivante :

On peut alors lancer le serveur en se rendant dans le répertoire applicatif :

cd EmberCellar
ember serve

Un navigateur pointant sur http://localhost:4200 affiche alors la page d’accueil du framework :

Comme mentionné dans le bas de page, cette page est rendue au travers d’un package d’initialisation qu’il s’agit d’enlever :

   To remove this welcome message, remove the ember-welcome-page add-on from your package.json file

On édite le fichier package.json et on redémarre le serveur Ember pour prendre en compte cette modification de dépendance.  

Ember CLI cherche une page dans app/templates/application.hbs. C’est une règle implicite d’Ember qui va chercher cette page par défaut

On édite cette page pour y intégrer le code HTML

<h1>Ember Cellar</h1>
   {{outlet}}

Automatiquement, la page se rafraîchit pour afficher le titre.

La ligne {{outlet}} sert de placeholder pour intégrer des pages filles sur le principe des poupées russes (on verra plus tard ce principe fondamental de fonctionnement).

Dans la suite de ce tutorial, on souhaite que l’URL /racks affiche la liste des racks présents dans la cave à vins. On ajoute une route permettant de l’atteindre :
ember generate route racks

On remarque qu’Ember CLI génère à la fois la route (app/routes/racks.js), un template (app/templates/racks.hbs) et des données de tests (tests/unit/routes/racks-test.js)

L’utilisation du générateur de route modifie également le fichier router.js pour y intégrer la nouvelle route :

// app/router.js
import Ember from 'ember';
import config from './config/environment';

const Router = Ember.Router.extend({
 location: config.locationType,
 rootURL: config.rootURL
});

Router.map(function() {
 this.route('racks');  // automatically added by ‘ember generate route’
});

export default Router;

L’URL http://localhost:4200/racks répond maintenant (en affichant cependant pas plus de choses que http://localhost:4200). Cette route recherche en effet un template racks.hbs dans le répertoire templates, fichier qui a été créé mais qui ne contient pour le moment que le placeholder {{outlet}}

Si on modifie le fichier app/templates/racks.hbs comme suit :

<h2>List of Racks</h2>
   {{outlet}}

On voit que le {{outlet}} de la page application.hbs va être remplacé par le template de la page racks.hbs → processing des URL http://localhost:4200 et http://localhost:4200/racks

Dans un premier temps, nous allons retourner un résultat mocké. Pour cela, on va s’appuyer sur le routeur spécifique au path ‘racks’ (répertoire ‘routes’, fichier ‘racks.js’) pour ajouter des données en sortie :

// app/routes/rack.js

import Ember from 'ember';

let racks = [
  {
    name: 'Standard grey Rack',
    nbColumns: 6,
    nbRows: 4,
    image: './img/rack_01.jpg'
  },
  {
     name: 'Traditional big Rack',
     nbColumns: 10,
     nbRows: 10,
     image: './img/rack_02.jpg'
  },
  {
    name: 'Small Wood Rack',
    nbColumns: 3,
    nbRows: 4,
    image: './img/rack_03.jpg'
  }
];

export default Ember.Route.extend({
  model() {
     return racks;
  }
});

On dépose par ailleurs dans le répertoire public/img (après création de ce dernier) les fichiers rack_01.jpg à rack_05.jpg référencés pour certains dans les données de tests.

Reste à customiser le template d’affichage pour tirer profit des informations retournées par le modèle :

<h2>List of Racks</h2>

<div class="detail">
{{#each model as |rack|}}
 <article class="listing">
   <h3>{{rack.name}}</h3>
   <div class="detail">
     <span>Nb Colums:</span> {{rack.nbColumns}}
   <span>Nb Rows:</span> {{rack.nbRows}}
   </div>
   <div class="detail">
     <img src={{rack.image}} width='235px' height='235px'/>
   </div>
 </article>
{{/each}}
{{outlet}}

</div>

Le résultat n’a rien de miraculeux, mais affiche notre liste de Racks de la façon la plus simpliste possible et sans fioriture décorative !

Step 1.2 – Un peu de beautifuling

Rendons l’affichage un peu plus sexy en s’appuyant sur des add-ons Ember. Ceux-ci sont extrêmement nombreux et assez bien référencés au travers du site https://www.emberaddons.com/ (privilégier les addons avec des notes à 9 ou 10 !).

En premier lieu, on peut utiliser un composant de table assez puissant et simple :model-tables. Et au passage, intégrer la bibliothèque Bootstrap :

ember install ember-models-table
ember install ember-bootstrap

Pour utiliser ce composant, on va avoir besoin d’un contrôleur, chargé notamment de déclarer les colonnes à afficher. On crée donc explicitement ce contrôleur (qui existait déjà de façon implicite) :
ember generate controller racks

Et dans le fichier créé, on déclare les colonnes et le modèle sous la forme :

// app/controllers/rack.js

import Ember from 'ember';

export default Ember.Controller.extend({

rackTableColumns: Ember.computed(function() {
var col = Ember.A([
   Ember.Object.create({
     propertyName: "name",
     title: "Name"
   }),
   Ember.Object.create({
    propertyName: 'nbColumns',
    title: 'Nombre de colonnes',
   }),
   Ember.Object.create({
    propertyName: 'nbRows',
    title: 'Nombre de lignes',
   }),
   Ember.Object.create({
      "template": "customcell/imagecell",
     title: 'Image',
   })
]);
return col;
}),

rackTableContent: Ember.computed(function() {
 return this.get("model");
})
});

A noter que l’affichage de l’image des racks passe par un template spécifique ‘customcell/imagecell’ que l’on doit créer dans le répertoire template et qui contient les lignes suivantes :

{{!`record` - row from model, current column is available as `column`}}
<img src='{{record.image}}' height="128" width="128"/>

On affiche alors notre table (avec tous les comportements d’interaction qu’elle supporte) en remplaçant la boucle présente dans le template racks.hbs par ces quelques lignes :

<h2>List of Racks</h2>
{{models-table
         data=rackTableContent
         columns=rackTableColumns}}
{{outlet}}

Et pour profiter un peu plus de Bootstrap, on retravaille la page principale ‘application.hbs’ pour y intégrer quelques classes spécifiques :

<div class="container">

  <div class="page-header">
    <h1>Ember Cellar</h1>
    <p class="lead">Basic layout using Bootstrap for a simple wine application</p>
  </div>
  {{outlet}}

</div>

On obtient immédiatement un rendu beaucoup plus professionnel et des possibilités d’interaction sur les données du tableau nettement plus poussée ;

Voilà, C’est déjà pas mal pour cet épisode !