<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Hadrien Lanneau]]></title><description><![CDATA[A french Front End Architect blog]]></description><link>http://blog.hadrien.eu/</link><generator>Ghost 0.10</generator><lastBuildDate>Mon, 05 Nov 2018 20:03:45 GMT</lastBuildDate><atom:link href="http://blog.hadrien.eu/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Suspense]]></title><description><![CDATA[<p>Suspense est une fonctionnalité apparue dans React 16.6 et permettant de gérer plus facilement l'asynchrone dans vos composants react. L'utilisation la plus basique qui est présentée dans la doc de React permet de se passer de react-loadable et de charger des modules dynamiquement sans bibliothèque tierce. Ainsi, le code</p>]]></description><link>http://blog.hadrien.eu/2018/11/05/suspense/</link><guid isPermaLink="false">97171f61-2f93-4304-a993-4754d09d5d8f</guid><category><![CDATA[React]]></category><category><![CDATA[suspense]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 05 Nov 2018 10:23:55 GMT</pubDate><content:encoded><![CDATA[<p>Suspense est une fonctionnalité apparue dans React 16.6 et permettant de gérer plus facilement l'asynchrone dans vos composants react. L'utilisation la plus basique qui est présentée dans la doc de React permet de se passer de react-loadable et de charger des modules dynamiquement sans bibliothèque tierce. Ainsi, le code suivant permet de générer des chunks (fichiers javascript compilé et chargé à la demande) pour chaque module de notre navigation :</p>

<pre><code class="language-javascript">import React, { lazy, Suspense } from 'react';  
import { Switch, Route } from 'react-router';

import Loading from './Loading';

const Home = lazy(() =&gt; import('./Home');  
const Foo = lazy(() =&gt; import('./Foo');

export const MainView = () =&gt; (  
  &lt;Switch&gt;
    &lt;Route path="" exact&gt;
      &lt;Suspense fallback={Loading}&gt;
        &lt;Home /&gt;
      &lt;/Suspense&gt;
    &lt;/Route&gt;
    &lt;Route path="/foo"&gt;
      &lt;Suspense fallback={Loading}&gt;
        &lt;Foo /&gt;
      &lt;/Suspense&gt;
    &lt;/Route&gt;
  &lt;/Switch&gt;
)
</code></pre>

<p>La fonction import() est standard et permet, selon la spécification ECMAScript, de charger des modules dynamiquement pendant l'exécution du code. La fonction est mise en pratique grâce à babel et webpack sous la forme de fichiers chunks : des fichiers pré-compilés séparés du bundle principal qui peuvent donc être chargés uniquement quand cela est nécessaire. Cela permet d'alléger le premier chargement de votre application en évitant de charger le code de vues que votre utilisateur n'ira jamais consulter.</p>

<p>Mais on peut aller plus loin.</p>

<h2 id="prchargementdedonnes">Pré-chargement de données</h2>

<p>Ce qui prend le plus de temps lorsqu'on charge une vue, ce n'est pas le module javascript qui n'est qu'un fichier statique de quelques centaines de Ko pour les plus gros, mis en cache par le navigateur. C'est le chargement des données initiales à faire afficher à notre vue. Prenons un exemple d'une vue de billet de blog. Le composant qui va afficher le billet est très simple : il se contente d'afficher la prop <code>title</code> et la prop <code>content</code>. Allez, on va l'alourdir un peu en lui faisans embarquer le package <code>markdown</code> car notre content sera en markdown. Le module et ses dépendances ne feront pas plus de quelques dizaines de Ko et sera chargé en moins de 40ms. Cependant, une fois le module chargé, le composant va aller regarder dans l'url quel est l'id du billet à charger et à afficher. S'en suit donc une requête xhr vers un backend plus ou moins lent nécessitant d'afficher un loader supplémentaire. <br>
Grâce à Suspense, il est possible de n'avoir qu'un seul et unique loader pour toute la séquence de chargement : composant + données. C'est ce que proposait AngularJS sous la forme des <em>resolvers</em> et Aurelia-router sous la forme de la méthode <code>activate</code> et qui m'a toujours manqué sur React.</p>

<p>L'idée est d'exporter en plus de notre vue une fonction qui retourne une promesse dont la résolution conviendra à <code>lazy</code>, à savoir : un object avec une propriété <code>default</code> de type <code>function</code>.</p>

<iframe src="https://codesandbox.io/embed/jl9m20lv29" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>

<p>Le composant <code>Test</code> est chargé dynamiquement, on attend sa résolution dont on extrait une fonction <code>withLazyData</code> préalablement exportée qu'on exécute et dont on retourne le résultat à <code>lazy</code>. <code>withLazyData</code> va faire son travail asynchrone de récupération de données, et retourne une promesse résolvant un objet dont la propriété <code>default</code> est notre composant avec les données passées en props. Ainsi, le composant généré par lazy ne s'affichera que lorsque le module sera chargé et que les données auront été passées au composant.</p>]]></content:encoded></item><item><title><![CDATA[Redux sux]]></title><description><![CDATA[<p>React et Redux sont pour beaucoup deux faces d'un tout immutables. Il est difficile de trouver une formation ou un projet où redux n'a pas été choisi comme outil de gestion d'état. Pourtant, force est de constater qu'il est la plupart du temps très mal utilisé. Nous allons d'abord voir</p>]]></description><link>http://blog.hadrien.eu/2018/10/29/redux-sux/</link><guid isPermaLink="false">9e466061-2be5-43d1-8155-a974710adb17</guid><category><![CDATA[React]]></category><category><![CDATA[redux]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 29 Oct 2018 08:02:26 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2018/10/redux-sux.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2018/10/redux-sux.jpg" alt="Redux sux"><p>React et Redux sont pour beaucoup deux faces d'un tout immutables. Il est difficile de trouver une formation ou un projet où redux n'a pas été choisi comme outil de gestion d'état. Pourtant, force est de constater qu'il est la plupart du temps très mal utilisé. Nous allons d'abord voir pourquoi j'estime que son usage dépasse son objectif, et nous verrons quelles meilleures solutions existent.</p>

<h2 id="thesinglesourceoftruth">The single source of truth</h2>

<p>Redux est conçu pour gérer l'état global d'une application. Il est prévu pour permettre de partager des informations entre différentes parties de l'application mais il incite surtout à faire passer absolument toutes les informations d'une app par ce store. En théorie, les composants react ne contiennent absolument aucune logique métier de traitement des données et se consacrent strictement à l'interface pendant que redux met à disposition des actions qui sauront manipuler les modèles et les publier dans ce store.</p>

<p>Les limites à cette description idyllique arrivent quand on commence à vouloir découper son application en modules réutilisables. Redux fonctionne avec un seul et unique store. Pour permettre de greffer des reducers provenant de modules extérieurs, il faudra donc combiner le reducer supplémentaire au store de son application. Bref, en plus de devoir importer les composants du module, il faut aussi aller câbler les actions et les reducers de ce module. Pas très plug'n'play. Nous verrons dans la seconde partie comment cela pourrait être bien plus simple avec des techniques standards. Ma quête vers la modularité a été grandement motivée par le fait qu'il semble pour beaucoup obligatoire de faire passer par redux la moindre des données, même si une requête est faite par un seul et unique composant dans toute l'app et qu'elle n'a évidemment aucun besoin de partager son résultat avec le reste de l'app.</p>

<p>Un constat que j'ai pu faire aussi est que redux semble trop souvent utilisé comme outil pour faire passer des infos dans des composants sans les faire passer de parents à enfants. Tout simplement car Redux est bâti depuis toujours sur l'API context de react, qui était jusqu'au mois de Mars 2018, officiellement déconseillée d'usage par l'équipe de React. Un développeur cherchant une solution pour contextualiser des données et se tournant vers la doc de React context se voyait afficher un message anxiogène lui indiquant que cette API était vouée à disparaitre et qu'il ne fallait absolument pas l'utiliser. Redux était ainsi une solution plus sereine (alors que pourtant tout aussi dangereuse) pour arriver à ces fins.</p>

<p>Et c'est ainsi que redux est utilisé pour stocker tout plein de trucs dans un gros store global. Autant balancer des variables globales dans window, ça reviendrait à peu près au même.</p>

<h2 id="modularit">Modularité</h2>

<p>Je suis très souvent confronté à des architectures où plusieurs sites se partagent de nombreux modules d'accès aux données et une API identique. J'ai donc un regard un peu plus global que : "un projet" => "une app". C'est pourquoi j'ai toujours perçu redux comme un frein. J'avais besoin de contextualiser des données, mais dans des scope limités. Et surtout, j'avais envie que mes modules soient utilisables en ne faisant rien de plus qu'importer les composants de ce module. J'ai donc, dès Mars 2018, regardé du côté de React Context. Celui-ci permet de générer un <code>Provider</code> et un <code>Consumer</code> dont le but est justement de faire passer des données à travers un arbre de composants. J'ai donc par exemple un module Authentification publiant un composant <code>AuthProvider</code> et un hoc <code>withAuth</code> qui permettront à mes apps de gérer l'authentification de la même manière. Je peux de plus placer mon <code>&lt;AuthProvider&gt;</code> au niveau le plus cohérent avec mon app : soit à la racine si toute mon app doit changer de comportement selon l'auth, soit uniquement au niveau des routes privées.</p>

<p>L'autre aspect qui me plait beaucoup avec cette approche, c'est qu'on peut le plus simplement du monde gérer ses actions et les mises à jour de l'état du Provider avec async/await sans se taper un immonde reducer.</p>

<p>Je vous laisse jeter un œil à cet exemple qui montre un provider de compteur asynchrone qui est consommé par un composant qui affiche la valeur du compteur, et un autre composant qui appelle l'action d'incrémentation asynchrone.</p>

<iframe src="https://codesandbox.io/embed/lrlz23226m" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>

<p>Cet usage de Context et de async/await me permet de plus facilement découper mes fonctionnalités par modules et de garder côte à côte les composants et leurs stores. Ce qui rend évidemment bien plus simple la publication de chacun de ces modules dans des packages npm afin qu'ils soient réutilisés en toute simplicité dans d'autres applications.</p>

<p>Et ce qui me permet d'écrire cet article alors que mon expérience en react/redux est largement plus courte que la plupart de la communauté (j'ai commencé en décembre dernier), c'est que le futur du React va s'orienter officiellement vers ce genre de pratique grâce aux hooks <a href="https://reactjs.org/docs/hooks-reference.html#usecontext"><code>useContext</code></a> et <a href="https://reactjs.org/docs/hooks-reference.html#usereducer"><code>useReducer</code></a> qui permettront, en natif, sans lib supplémentaires, de gérer des stores modulaires tout en gardant la liberté de choisir le pattern de reducer ou de l'async/await.</p>]]></content:encoded></item><item><title><![CDATA[Express GraphQL]]></title><description><![CDATA[<p>J'ai eu besoin de monter un rapide backend pour un petit site statique auquel je devais ajouter une API de blog légère. On est en 2018, j'ai donc décidé de monter cette API avec GraphQL. <a href="http://graphql.github.io">GraphQL</a>, c'est un protocole proposé par facebook permettant de construire des requêtes complexes, particulièrement efficace</p>]]></description><link>http://blog.hadrien.eu/2018/08/31/express-graphql/</link><guid isPermaLink="false">2953e30c-c0e3-4850-8396-d59848bd46ed</guid><category><![CDATA[Javascript]]></category><category><![CDATA[architecture]]></category><category><![CDATA[API]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Fri, 31 Aug 2018 09:21:10 GMT</pubDate><media:content url="http://graphql.github.io/img/logo.svg" medium="image"/><content:encoded><![CDATA[<img src="http://graphql.github.io/img/logo.svg" alt="Express GraphQL"><p>J'ai eu besoin de monter un rapide backend pour un petit site statique auquel je devais ajouter une API de blog légère. On est en 2018, j'ai donc décidé de monter cette API avec GraphQL. <a href="http://graphql.github.io">GraphQL</a>, c'est un protocole proposé par facebook permettant de construire des requêtes complexes, particulièrement efficace lorsqu'il s'agit de récupérer une arborescence de données. Dans mon cas, j'aurais pu me contenter d'un rapide REST mais qu'aurais-je appris alors ? Pour la petite histoire, mon cas s'est finalement arrêté sur Firebase de Google, qui méritera lui aussi un bel article, mais ma recherche n'aura pas été vaine car elle aura au moins servi à produire cet article.</p>

<p>Nous allons donc construire un backend en nodejs qui servira une API GraphQL à l'aide de <a href="https://github.com/graphql/express-graphql">express-graphql</a>. Vous pouvez déjà consulter <a href="https://github.com/hadrienl/express-graphql-exemple">les sources de mon exemples</a>.</p>

<p>Je ne vais pas traduire la doc de express-graphql qui est vraiment très claire. Je vais simplement approfondir les sujets les plus importants (ceux pour lesquels j'ai du relire plusieurs fois la partie dédiée de la doc 😅)</p>

<h2 id="usage">Usage</h2>

<p>Il est bon de commencer par comprendre comment on va utiliser notre API. Ça peut être pratique pour la tester, surtout si, comme moi, vous n'avez encore jamais utilisé ce type d'API.</p>

<p>On communique avec un serveur GraphQL par le biais de requêtes POST. Dans le body de ces requêtes, on attache un objet qui va décrire notre requête. Cet objet contient deux paramètres importants : <code>query</code> et <code>variables</code>. Le premier, c'est notre requête GraphQL, le second, c'est une liste de variables qui seront passées à la requête. Une fois qu'on a compris ça, on peut s'en foutre puisqu'on va utiliser des libs qui gèrent ça très bien toutes seules.</p>

<p>Ce qui est très pratique avec express-graphql, c'est qu'il inclut en option une webui permettant de se requêter lui même. Lorsque vous construisez votre serveur GraphQL, vous avez la possibilité d'activer ce serveur via le paramètre <code>graphiql</code> :</p>

<pre><code class="language-[language-javascript]">const graphql = graphqlHTTP({  
  schema,
  rootValue,
  graphiql: true,
});
</code></pre>

<p>Dans ce cas, lorsque vous taperez votre API d'un GET, vous obtiendrez une page html matérialisant une console grâce à laquelle vous allez pouvoir jouer avec votre API en cours de construction :</p>

<p><img src="http://blog.hadrien.eu/content/images/2018/08/Capture-d--cran-2018-08-31-09.58.00.png" alt="Express GraphQL"></p>

<p>Pensez à la désactiver en prod.</p>

<h2 id="query">Query</h2>

<p>Le serveur s'attend donc à recevoir une <code>query</code> dans un format particulier. D'où le QL de GraphQL. Cette requête va donc décrire précisément la structure de données que vous espérez recevoir. Imaginons que nous voulons recevoir le <code>user</code> connecté ainsi que ses 10 derniers <code>posts</code> publiés. Avec une API REST, on va faire deux requêtes, la première pour avoir le <code>user</code>, la seconde pour avoir les <code>posts</code> du <code>user</code>. Sauf si côté backend on a pensé à faire un endpoint pour avoir ça d'un seul coup. Mais c'est pas documenté alors on sait pas. Et puis les posts, on les veut juste pour afficher le titre donc si on pouvait se passer de charger tout leur contenu, ça serait mieux. Bon. Avec GraphQL, on va juste demander ce dont on a besoin. On veut le prénom du user pour lui dire bonjour, et les titres et le slug de ses 10 derniers posts pour lui faire une liste de liens :</p>

<pre><code>{
  me {
    firstName
    posts(page: 1, perPage: 10, orderBy 'publishedAt') {
      title
      slug
    }
  }
}
</code></pre>

<p>Et je recevrais alors un objet de ce genre :</p>

<pre><code class="language-[language-javascript]">{
  firstName: 'Georges',
  posts: [{
    title: 'Monde de merde',
    slug: 'monde-de-merde',
  }, /*x10*/]
}
</code></pre>

<p>Avouez que c'est assez pratique.</p>

<p>Côté backend, on va donc devoir définir un schéma pour pouvoir cadrer nos requêtes et renvoyer automatiquement les erreurs qui permettront au consommateur de l'API de corriger sa requête tout seul comme un grand. Ensuite, nous n'aurons plus qu'à lui attacher des <code>resolvers</code>, des méthodes qui vont répondre à chacune de nos définitions de requêtes.</p>

<h2 id="schema">Schema</h2>

<p>La première étape est donc de définir un schéma d'API. On va définir des structures typées et des structures de requêtes. On pourra voir par la suite qu'on peut utiliser tous les outils qu'offrent tous langages de programmation typés telles que les interfaces par exemple, mais on va rester simple pour le moment. Notre schéma pour la requête du dessus va contenir 3 <code>type</code> : <code>User</code>, <code>Post</code> et <code>Query</code>. Les deux premiers vont décrire toutes les propriétés que peuvent avoir ces deux entités. Le dernier va décrire les méthodes qui peuvent être appelées dans une requête, leurs paramètres et le format de leurs réponses.</p>

<p>Ça donnera un truc dans ce genre :</p>

<pre><code>{
  type Post {
    id: String!
    title: String!
    content: String!
    slug: String!
  }

  type User {
    id: String!
    firstName: String
    lastName: String
    posts: [Post]
  }

  type Query {
    me: User
    user(id: String!): User
  }
}
</code></pre>

<p>Donc on voit que chaque attribut de chaque <code>type</code> est typé. On voit aussi que certains types ont un <code>!</code>. Ça signifie juste que la valeur doit être non nulle. On voit aussi que certains <code>type</code> peuvent faire appel à d'autres. Ainsi, <code>User</code> possède un attribut dont la valeur est un tableau de <code>Post</code>. Pour finir le type <code>Query</code> propose deux exemples différent. Le premier est en fait une méthode sans paramètre. On va juste demander à recevoir <code>me</code> (donc "moi") et on recevra un objet de type User me représentant. Le second prend un paramètre nommé <code>id</code> et obligatoire (<code>!</code>) et retournera un objet de type User représentant la ressource correspondant à l'ID demandé.</p>

<p>Tout cela est en réalité bien plus complexe et puissant, mais le but était juste d'avoir une idée globale du concept.</p>

<h2 id="rsolveurs">Résolveurs</h2>

<p>Oui j'adore traduire le maximum de termes anglais. On va pour finir écrire des fonctions qui seront exécutées pour récupérer les données. Ainsi, quand on demande un user et ses posts, on ira peut être chercher le user dans un PostGreSQL, et les posts dans un MongoDB : les deux types de données seront récupérées en parallèle. Avec express-graphql, c'est donc le second attribut de la fonction <code>graphqlHTTP</code> : <code>rootValue</code>. Il s'agit d'un objet listant les méthodes du type <code>Query</code> spécifiés dans le schema passé en premier paramètre.</p>

<p>Ces méthodes prendront deux paramètres : le premier est un objet contenant les variables passées à la requête. Ainsi, si je demande :</p>

<pre><code>{
  user(id: 42) {
    firstName
  }
}
</code></pre>

<p>Alors :</p>

<pre><code class="language-[language-javascript]">{
  user({ id }) {
    return UserService.getById(id);
  }
}
</code></pre>

<p><code>id</code> vaudra <code>42</code>.</p>

<p>On pourra grâce au second paramètre accéder à l'objet de requête express afin d'aller récupérer des infos sur la requête et notamment la session d'authentification :</p>

<pre><code class="language-[language-javascript]">{
  me(_, { user }) {
    return UserService.getById(user.id);
  }
}
</code></pre>

<p>Et du coup, pour finir avec en bonus, comment gérer l'auth, même si ça sort du sujet de l'article : petit rappel pour ceux qui n'ont pas fait d'express depuis bien trop longtemps comme moi.</p>

<h2 id="authetautresmiddleware">Auth et autres middleware</h2>

<p>Pour gérer l'authentification, on utilise un middleware et on va ajouter des données dans l'objet <code>req</code> qui sera passé de middleware en middleware jusqu'à arriver à GraphQLHTTP. Ainsi, si on veut qu'un token soit passé à la requête en querystring, celui-ci permettant de décoder un ID de user, alors on configurera notre app de la sorte :</p>

<p>Le middleware d'authentification :</p>

<pre><code class="language-[language-javascript]">function auth(req, res, next) {  
  const { query: { token } = {} } = req;

  if (token !== '1234') { // le token est hardcodé parce que la sécurité c'est pas très important de nos jours
    return res.sendStatus(401);
  }

  req.user = UserService.getByToken(token);

  return next();
}
</code></pre>

<p>Qu'on placera donc avant GraphQL :</p>

<pre><code class="language-[language-javascript]">const app = express();  
app.use('/', auth, graphql);  
</code></pre>

<p>Et voilà, si le token "1234" n'est pas passé, alors on n'accèdera pas du tout à GraphQL. Dans le cas contraire, GraphQL aura accès à la propriété <code>user</code> savamment placée dans <code>req</code>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Il est finalement possible d'architecturer à loisir votre code. Dans mon exemple, j'ai fait le choix d'avoir un seul endpoint (<code>/</code>) pour accéder à toutes mes données et de découper mon schema en plusieurs morceaux. On peut faire autant de endpoints qu'on veut (allez donc lire la doc d'express si vous ne savez pas comment faire), ce qui peut être notamment pratique si on veut donner à accès à certaines requêtes en public et on peut aussi n'avoir qu'un seul gros fichier pour définir le schéma complet. Et il est aussi possible d'exporter le schema définitif grâce à des outils dédiés pour servir de documentation.</p>

<p>Il est évidemment aussi possible de construire un serveur GraphQL avec n'importe quel autre langage qui s'exécute sur un serveur.</p>]]></content:encoded></item><item><title><![CDATA[Pourquoi je ne teste pas ?]]></title><description><![CDATA[<p>Tester son code est une étape primordiale quand on vise une base de code pérenne. Les tests permettent de détecter en même temps que le développement les éventuels bugs et régressions que les nouvelles fonctionnalités pourraient apporter. Mais écrire des tests, c'est long et c'est pas très rigolo, surtout quand</p>]]></description><link>http://blog.hadrien.eu/2018/07/27/pourquoi-je-ne-teste-pas/</link><guid isPermaLink="false">0811eceb-2099-4f7b-b0a3-7120496cc1a2</guid><category><![CDATA[unit-tests]]></category><category><![CDATA[Javascript]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Fri, 27 Jul 2018 10:00:00 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2018/07/unit-tests-broken.gif" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2018/07/unit-tests-broken.gif" alt="Pourquoi je ne teste pas ?"><p>Tester son code est une étape primordiale quand on vise une base de code pérenne. Les tests permettent de détecter en même temps que le développement les éventuels bugs et régressions que les nouvelles fonctionnalités pourraient apporter. Mais écrire des tests, c'est long et c'est pas très rigolo, surtout quand on est développeur de guichet (<em>front-end</em> pour les non initiés à l'Académie Française). Et puis bon, je donne des leçons, mais moi aussi il m'arrive de ne pas tester mon code. Voici pourquoi.</p>

<p><img src="http://blog.hadrien.eu/content/images/2018/07/Wrote-A-Bunch-Of-Code-Without-Testing-530.jpg" alt="Pourquoi je ne teste pas ?"></p>

<h2 id="parcequejaipasletemps">Parce que j'ai pas le temps !!!!</h2>

<p>Oui, souvent la date de livraison du projet est très courte et il faut aller très vite. Tester son code en live dans son navigateur, puis sur d'autres navigateurs est déjà très chronophage. Rédiger des tests avant d'être sur que le DOM généré sera celui qui correspondra à la maquette sur tous les navigateurs supportés peut aussi être une source de perte de temps. Oh et puis, on est agile, donc quand on commence un développement, il est très probable qu'avec l'agilité de la biche, on nous demande de changer des trucs en cours de route. Du coup, j'ai plutôt tendance à rédiger les tests à la fin. Mais à la fin, souvent, j'ai plus le temps parce qu'il fallait livrer hier.</p>

<h2 id="parcequecestmoche">Parce que c'est moche !!!!</h2>

<p>Alors ici, c'est un argument vraiment typique du développeur front. Ce qui fait la différence entre un développeur de guichet et d'arrière guichet (back end), c'est sa sensibilité aux belles choses. Le backend lui, il s'en fout du rendu. Tant que sa donnée est correctement transformée comme il l'a décidé ou que sa commande agit tel qu'il le lui a ordonné, un terminal fera aussi bien l'affaire qu'un design léché. Et donc, finalement, les tests, c'est même carrément sexy pour lui. Le front dev, lui, il développe du design, du graphique, du visuel, de l'ergonomie, du feeling, des choses difficilement appréciable autrement qu'à l'usage. C'est d'ailleurs souvent pendant le développement qu'on se rend compte que certains détails de la maquette figée en jpg ne sont pas cohérents et qu'on fait alors appel aux instincts de biche du développeur pour modifier et améliorer la situation. Donc finalement, on est déjà en alternance entre dev et tests du simple fait d'aller voir le résultat de son dev sur son navigateur. Et une fois le résultat accompli, on est content de voir ces belles choses s'afficher et on a pas vraiment envie de doubler l'effort pour voir des trucs moches dans un terminal qui ne correspondent à rien de concret.</p>

<h2 id="parcequecestcompliqu">Parce que c'est compliqué !!!!</h2>

<p>Cette fois, ça vaut aussi pour le dev d'arrière guichet, mais moins, car lui n'a pas vraiment le choix finalement. Il est obligé de passer par des tests s'il veut constater que son code fonctionne. Donc pour les raisons évoquée au dessus, le front dev arrive à se passer de tests, et donc, il en fait peu et donc n'apprend pas à en faire 😅 Et donc c'est compliqué, et paf, une nouvelle excuse ! Faut avouer aussi que ça ne s'apprend pas en claquant des doigts de déterminer quoi tester, comment le tester, savoir délimiter la portée de son test, savoir ce qu'est un mock, comment l'écrire, etc.</p>

<h2 id="parcequejesuisleseuldev">Parce que je suis le seul dev !!!!</h2>

<p>Bah je dois déjà m'occuper tout seul de l'intégration, du SEO, de l'architecture front et puis aussi aider à l'infrastructure parce que AWS c'est pas si simple que le boss le pensait alors pfiou… je vais pas m'emmerder avec des tests. Oui certes, le jour où je me retrouve face à une régression, je vais encore perdre plus de temps, mais je suis certain que d'ici là on aura enfin embauché quelqu'un pour s'occuper du support !</p>

<h2 id="parcequejelevautbien">Parce que je le vaut bien !!!!</h2>

<p>C'est bien connu, celui qui teste doute. Moi je code comme un Dieu, j'ai pas besoin de test. J'ai vu mon dev fonctionner dans mon navigateur donc <strong>works4me</strong>. Si on vient me remonter un bug, c'est probablement parce qu'un autre dev est passé derrière et code moins bien, je vais plutôt aller lui donner des coups de fouets et l'insulter, ensuite, on pourra aller voir pourquoi ça merde sous IE depuis l'ajout de cette nouvelle fonctionnalité.</p>

<p><img src="http://blog.hadrien.eu/content/images/2018/07/unit-tests-broken-1.gif" alt="Pourquoi je ne teste pas ?"></p>

<h2 id="parcequeeeeeeeee">PARCE QUEEEEEEEEE</h2>

<p>Dans les faits, il n'y a que peu d'excuses valables pour ne pas faire de tests. C'est plus naturel pour un développeur d'arrière guichet qui n'a pas beaucoup d'autre moyen pour constater que son code fonctionne. Pour un développeur de guichet, c'est le navigateur qui joue ce rôle et les tests sont une étape supplémentaire à rajouter au worflow qui est déjà très copieux. Cependant, les outils pour mettre en place les tests sont aujourd'hui fournis clé en main à l'aide d'un simple <code>npm i</code>. Il faut s'y mettre et prendre cette habitude. Même si parfois les circonstances font que c'est un peu plus compliqué. Il faut l'assumer et faire tout son possible (en tant que dev et en tant que manager) pour permettre d'améliorer la situation dans les projets futurs. </p>]]></content:encoded></item><item><title><![CDATA[Injection de props via un provider de context]]></title><description><![CDATA[<p><em>(le titre est vraiment à chier</em> 😅<em>)</em></p>

<p>Aujourd'hui, je travaille sur un projet consistant à produire une codebase capable de générer des dizaines de sites très semblables au niveau de la structure mais qui possèdent chacun leurs particularité. Jusqu'à maintenant, ce travail s'est fait essentiellement en forkant le précédent site et</p>]]></description><link>http://blog.hadrien.eu/2018/07/09/injection-de-props-via-un-provider-de-context/</link><guid isPermaLink="false">bd15ec33-9410-4860-ba7a-b3c8c6b05bad</guid><category><![CDATA[React]]></category><category><![CDATA[framework]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 09 Jul 2018 18:06:19 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2018/07/react.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2018/07/react.jpg" alt="Injection de props via un provider de context"><p><em>(le titre est vraiment à chier</em> 😅<em>)</em></p>

<p>Aujourd'hui, je travaille sur un projet consistant à produire une codebase capable de générer des dizaines de sites très semblables au niveau de la structure mais qui possèdent chacun leurs particularité. Jusqu'à maintenant, ce travail s'est fait essentiellement en forkant le précédent site et donc en produisant des sites jetables très difficiles à maintenir. React m'a aidé à mettre en place des composants configurables à plusieurs niveaux et ainsi à générer un site contenant simplement un composant et une configuration. Ce composant génèrera toutes les pages communes à tous les sites, les routes, etc tout en injectant les composants spécifiques au site. Mais comment arriver à injecter tout ça dans les tréfonds de cet énorme composant ?</p>

<p>Ce que je trouve vraiment très puissant avec React, c'est l'usage quasi illimité qu'on peut faire avec les <code>props</code>. Les props sont les entrées du composant qui vont être utilisées pour générer une sortie. Ces props sont passées au composant lors de son implémentation, mais là où ça devient très puissant, c'est qu'on peut aussi passer ces props par le biais de HOC, des fonctions qui vont se placer de façon transparente au dessus du composant ou alors via la définition des props par défaut (<code>defaultProps</code>). Avec ces 3 outils, on est capable de faire des composants à l'implémentation ultra simpliste mais pourtant très facilement réutilisable ou configurables.</p>

<h2 id="injectionparlesprops">Injection par les props</h2>

<p>La grosse difficulté que j'ai pu découvrir lors de l'apprentissage de React est venue de la volonté de vouloir reproduire ce que je connaissais d'autres framework plus proches de la spec des WebComponent. Notamment, mon premier gros <a href="https://fr.wiktionary.org/wiki/pédi-">pédicode</a> a été de vouloir reproduire des slots pour pouvoir passer plusieurs éléments enfants à mon composant. Quelqu'un avait cherché à faire la même chose et avait obtenu une réponse sur StackOverflow, j'ai donc pensé que c'était légitime de procéder de la sorte. Puis plus tard, j'ai compris qu'il était possible et surtout bien plus pratique de passer des composants directement via les props. Il devenait ainsi possible d'injecter absolument ce qu'on voulait à un composant à condition que celui-ci soit conçu de façon à être à l'écoute de ces injections.</p>

<p>Un autre des défauts que je trouvais à React était sa non séparation du rendu et de la logique dans la plupart des tutos. Et c'est ici que j'ai pu trouver une réponse à cette problématique et concevoir tous mes composants sous la forme de deux fichiers : un index.js et un render.js. Le premier s'occupant de récupérer toutes les données nécessaires à passer au render qui lui n'était qu'une bête fonction retournant directement et sans la moindre logique du VDOM. J'injecte ce render à mon index via les props, ce qui me permet de pouvoir changer le rendu du composant simplement en passant une autre fonction à la prop render de mon composant.</p>

<p>Voici donc le modèle de composant que j'utilise :</p>

<pre><code class="language-[language-javascript]">// MonComposant/index.js
import React from 'react';

import render from './render';

export class MonComposant extends React.Component {  
  static defaultProps = {
    render,
  };

  render() {
    const { render: Render, ...nextProps } = this.props;
    // On peut ici générer ou altérer des props à passer au render
    const label = 'World';
    const props = {
      ...nextProps,
      // On ajoute ici les props générés à passer au render
      label,
    };

    return &lt;Render {...props} /&gt;;
  }
};

export default MonComposant;  
</code></pre>

<pre><code class="language-[language-javascript]">// MonComposant/render.js
import React from 'react';

export const MonComposantRender = ({ label }) =&gt; (  
  &lt;p&gt;Hello {label}&lt;/p&gt;
);
export default MonComposantRender;  
</code></pre>

<p>Premier avantage : on se retrouve avec un modèle de séparation rassurant quand on vient du MVC, le template est à part du code. Mais le second avantage, c'est que cette fonction de rendu devient très facilement remplaçable. Ainsi, je pourrais implémenter mon composant de ces multiples façons pour avoir autant de résultats différents sans toucher au code du composant :</p>

<pre><code class="language-[language-javascript]">&lt;MonComposant /&gt;  
&lt;MonComposant  
  render={({ label }) =&gt; (
    &lt;h1&gt;Yo {label}&lt;/h1&gt;
  )} /&gt;
&lt;MonComposant  
  render={({ prefix, label }) =&gt; (
    &lt;h1&gt;{prefix} {label}&lt;/h1&gt;
  )}
  prefix="Yo" /&gt;
&lt;MonComposant  
  render={HelloComponent} /&gt;
</code></pre>

<p>Toute la logique est préservée, et je peux même injecter d'autres props à mon render personnalisé via le scope parent.</p>

<p>Je peux aussi utiliser ce modèle pour injecter des composants à mon render tout en facilitant leur modification ou leur mocking. En effet, dans le cas suivant où mon render va implémenter le composant <code>MonHeader</code>, je vais avoir tendance à importer <code>MonHeader</code> dans le render. Ce composant est alors figé dans le marbre, rendant la personnalisation impossible :</p>

<pre><code class="language-[language-javascript]">// render.js
import React from 'react';  
import MonHeader from './MonHeader;

export default () =&gt; (  
  &lt;div&gt;
    &lt;MonHeader /&gt;
  &lt;/div&gt;
)
</code></pre>

<p>Alors qu'il est tout à fait possible d'injecter ce composant via l'index.js et même de le rendre configurable :</p>

<pre><code class="language-[language-javascript]">// index.js
import React from 'react';  
import MonHeader from './MonHeader;  
import render from './render;

export default class MonComposant extends React.Component {  
  static defaultProps = {
    render,
    HeaderComponent: {
      component: MonHeader,
      title: 'Foo bar',
    },
  };

  render() {
    const { render: Render, HeaderComponent, ...nextProps } = this.props;
    const Header = () =&gt; &lt;HeaderComponent.component {...{...HeaderComponent, ...this.props}};
    // Là c'est la version complexe dans le cas où on veut faire transiter directement des props depuis MonComposant jusqu'à HeaderComponent
    // Mais si on veut juste injecter un composant, on peut faire beaucoup plus simplement :
    // const Header = HeaderComponent.component;
    const props = {
      ...nextProps,
      Header,
    };
    return &lt;Render {...props} /&gt;;
  }
}
</code></pre>

<pre><code class="language-[language-javascript]">// render.js
import React from 'react';

export default ({ Header }) =&gt; (  
  &lt;div&gt;
    &lt;Header /&gt;
  &lt;/div&gt;
)
</code></pre>

<pre><code class="language-[language-javascript]">// app.js
// Remplace complètement le composant
&lt;MonComposant  
  HeaderComponent={{
    component: CustomHeader
  }} /&gt;
// Remplace uniquement la valeur du `title` passé au composant par défaut
&lt;MonComposant  
  HeaderComponent={{
    title="Bar foo"
  }} /&gt;
// Remplace uniquement la fonction de render du composant par défaut, qui va donc prendre en props les props par défaut
&lt;MonComposant  
  HeaderComponent={{
    render={({ title }) =&gt; &lt;p&gt;{title}&lt;/p&gt;},
  }} /&gt;
</code></pre>

<p>Je peux juste tester mon render.js en lui passant un mock de Header en prop, je peux tester le composant entier en passant aussi un mock de Header en prop, et je peux passer un Header personnalisé si j'ai besoin d'afficher quelque chose de différent dans certains cas.</p>

<h2 id="configurationparcontexte">Configuration par contexte</h2>

<p>Maintenant, allons plus loin avec les hoc et context. Le plan pour que mon objectif soit atteint était d'arriver à générer un site avec seulement ceci :</p>

<pre><code class="language-[language-javascript]">render() {  
  return (
    &lt;SiteComponent
      config={config} /&gt;
  );
}
</code></pre>

<p>Rien de plus, rien de moins. Cependant, comment faire si sur l'un des sites, on ne veut pas afficher l'avatar dans le cadre des informations utilisateurs de la vue Dashboard accessible depuis la route <em>/dashboard</em> ? Grâce au context, et aux hoc.</p>

<p>Mon composant est accompagné d'un Provider qui se charge de récupérer la configuration et de la ranger dans un Context. Ainsi, chacun de mes composants passe par un hoc <code>withSiteConfig</code> donnant accès à la configuration. Mais j'ai aussi un hoc <code>withAutoConfig(path)</code> prenant en paramètre le chemin vers la configuration du composant. Les props du composant seront alors automatiquement remplies en piochant dans la config passée au Provider.</p>

<p>Ainsi, si je veux personnaliser le composant UserCard de la vue Dashboard, je pourrais implémenter mon site de la sorte :</p>

<pre><code class="language-[language-javascript]">render() {  
  return (
    &lt;Provider
      config={{
        Dashboard: {
          UserCardComponent: {
            component: CustomUserCard,
          },
        },
      }}&gt;
      &lt;SiteComponent /&gt;
    &lt;/Provider&gt;
  );
}
</code></pre>

<p>ou bien plus simplement :</p>

<pre><code class="language-[language-javascript]">render() {  
  return (
    &lt;Provider
      config={{
        Dashboard: {
          UserCardComponent: {
            render: ({ user }) =&gt; &lt;p&gt;Hello {user.name}&lt;/p&gt;,
          },
        },
      }}&gt;
      &lt;SiteComponent /&gt;
    &lt;/Provider&gt;
  );
}
</code></pre>

<p>ou encore plus simplement, si le composant le permet, en modifiant ses props :</p>

<pre><code class="language-[language-javascript]">render() {  
  return (
    &lt;Provider
      config={{
        Dashboard: {
          UserCardComponent: {
            displayAvatar: false,
          },
        },
      }}&gt;
      &lt;SiteComponent /&gt;
    &lt;/Provider&gt;
  );
}
</code></pre>

<p>Mon composant UserCard, lui, sera implémenté de la sorte :</p>

<pre><code class="language-[language-javascript]">import React from 'react';  
import { withAutoConfig } from '../config';

export class UserCard extends React.Component {  
  /**/
}

export default  
  withAutoConfig('views.Dashboard.UserCardComponent')(
    UserCard
);
</code></pre>

<p>Je ne vous présente pas le code de <code>withAutoConfig</code>, mais en gros son fonctionnement est le suivant : il récupère la config du contexte, les <code>propTypes</code> et les <code>defaultProps</code> du composant passé en paramètre, et pour chaque <code>propType</code>, il va ajouter la valeur correspondant à la prop prise par ordre de priorité dans la config, dans la config par défaut du projet puis dans le <code>defaultProps</code>.</p>

<p>En conclusion de cet article, je voudrais saluer la créativité que permet React. C'est probablement pour cet aspect que je m'y suis plu si rapidement. Il est possible d'architecturer ses projets de pleins de façons différentes, que ça soit par des hocs, par des renderProps, des context, autour de redux, etc. Il n'y a pas qu'un seul modèle à suivre bêtement comme une IA. Avec les effets négatifs qui sont qu'on peut aussi produire un énorme plat de pâtes impossible à maintenir 😅</p>]]></content:encoded></item><item><title><![CDATA[sudo shutdown -r now]]></title><description><![CDATA[<p>Waw… quasiment deux ans que je n'ai rien rédigé sur ce blog… Il est temps de rémédier à ça. Avant de repartir sur des posts techniques et classique, je vais juste raconter un peu ma vie concernant ces deux années passées.</p>

<p>En Septembre 2016, je démarrais une nouvelle expérience chez</p>]]></description><link>http://blog.hadrien.eu/2018/07/03/sudo-shutdown-r-now/</link><guid isPermaLink="false">1dfd9794-dd15-4451-b061-0a0c08985de4</guid><category><![CDATA[React]]></category><category><![CDATA[aurelia.io]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Tue, 03 Jul 2018 07:53:00 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2018/07/react-cover-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2018/07/react-cover-2.jpg" alt="sudo shutdown -r now"><p>Waw… quasiment deux ans que je n'ai rien rédigé sur ce blog… Il est temps de rémédier à ça. Avant de repartir sur des posts techniques et classique, je vais juste raconter un peu ma vie concernant ces deux années passées.</p>

<p>En Septembre 2016, je démarrais une nouvelle expérience chez Sport Heroes Group, chez qui j'apportais mes connaissances du framework Aurelia afin de remettre à neuf les architectures vieillissantes de leurs sites, basés sur un AngularJS orchestré par Grunt. J'ai conçu plusieurs sites qui ont pivoté très fréquemment en mode agile extreme grâce à Aurelia. Tout n'a pas été simple, il a fallu forker parfois, et la mutualisation des composants n'a pas pu être faite autrement qu'en dupliquant du code entre projets. Cependant, j'ai réussi à obtenir une base de code commune à plusieurs projets clients et un projet en SaaS, ce qui n'était pas une mince affaire compte tenu des spécificités de chaque projet, tant en terme produit qu'en terme d'hébergement.</p>

<p>Expérience très enrichissante, mais assez épuisante de part le nombre de projets à suivre et maintenir en même temps.</p>

<p>Malgré cela, Aurelia n'a pas su convaincre mon équipe avec qui je travaillais à distance à plein temps. C'est probablement ce point qui m'a empêché de suffisamment éduquer et faire adopter ce framework au reste de l'équipe qui est majoritairement resté fidèle (par la force des délais incompressibles aussi) à AngularJS. Ces mêmes délais qui ne me laissaient que peu de temps pour améliorer l'existant, m'empêchant par exemple de migrer les sites Aurelia à webpack, ce qui nous aurait très probablement permis de mutualiser très simplement des composants entre plusieurs projets.</p>

<p>C'est face à cet objectif non atteint que fut décidé en Décembre 2017 que toute l'équipe se mette à React. React, librairie que j'abhorrais depuis toutes ces années pour des principes relatif au respect des standards du web : syntaxe jsx non standard, pattern fonctionnels très éloignés de ce que j'avais eu l'habitude d'utiliser depuis des années : MVC, MVVM… je préférais utiliser des outils se rapprochant au plus des spécifications des web components : aurelia, évidemment, mais aussi vuejs par exemple. Et donc, j'ai été forcé de développer avec cet outil, finalement bien méconnu de ma part.</p>

<p>Et j'ai adoré.</p>

<p>Cela fait maintenant sept mois que je ne fais plus que du React, mon seul regret sera de ne pas m'y être mis bien plus tôt. Sans pour autant renier mes autres frameworks de prédilection, je reconnais sans soucis pourquoi React a réussi à prendre sa place de leader dans l'univers du développement web. Parmi les avantages que je peux retenir, voici les principaux :</p>

<ul>
<li>Réutilisabilité de toutes les parties du code</li>
<li>Atomisation des composants à l'extreme</li>
<li>Simplicité à personnaliser des composants grâce aux props et aux infinies façons de les utiliser</li>
<li>JSX inclus dans Babel : il est plus facile de démarrer un projet React que n'importe quel autre framework</li>
<li><strong>REACT NATIVE</strong></li>
</ul>

<p>Évidemment, tout n'est pas rose : React apporte aussi quelques désagrément qu'il faudra détecter le plus rapidement possible pour éviter de générer des tas de legacy impossibles à maintenir. En effet, l'un des arguments qui me poussaient à utiliser Aurelia plutôt que React était la séparation claire entre la logique et les templates html. Quand l'équipe est constituée de développeurs javascript, ce n'est pas un problème. Mais quand des intégrateurs partagent notre dure tâche, ils ont des réflexes tout à fait justifiables qui ne sont pas possible (et même extrêmement dangereux) en react. Ils vont taper dans le DOM, tenter de le manipuler, intégrer des plugins jquery… Mais attention ! L'intégrateur n'est pas le seul à commettre des erreurs. Le développeur expérimenté trouvera aussi des obstacles à affronter. Il sera coupable de produire des composants beaucoup trop complexes ou trop spécifiques. C'est plus un problème général, certes : le soucis de généricité du code est, à mon avis, l'une des plus grosses lacunes des développeurs. Mais la façon dont React permet de résoudre ces problématique quand on comprend son fonctionnement ne donne plus droit à l'excuse.</p>

<p>Vous aurez donc compris que j'ai véritablement retourné ma veste concernant React et que j'ai rejoint cette communauté avec plaisir. C'est mon blog qui n'est pas très content : j'arrive après la bataille et j'ai la sensation qu'il n'y a plus rien à dire. En effet, si j'ai pu progresser sur cette techno aussi rapidement, c'est aussi parce que je n'ai eu aucune difficulté à trouver de la doc et des tutos sur le web. Ainsi, j'ai beaucoup de mal à trouver des sujets à rédiger sur cette thématique sans passer pour un gros noob 😅 Mais j'ai quand même un sujet qui me semble très intéressant qui arrivera dans les prochains jours. <br>
J'espère aussi pouvoir tester d'autres outils dans le prochain poste que je démarrerais dans un futur proche : vuejs, stenciljs, etc.</p>

<p>Bon allez, j'ai des composants à refacto !</p>]]></content:encoded></item><item><title><![CDATA[Aurelia : Tester ses Custom Elements]]></title><description><![CDATA[<p>Tester un service est assez simple. Il suffit de l'importer et de l'instancier en lui passant des mocks de dépendances. Par contre, pour les Custom Elements, c'est plus compliqué car il faut le faire passer à travers le moteur de Aurelia. Pour cela, nous avons donc le module <code>aurelia-testing</code>, inclus</p>]]></description><link>http://blog.hadrien.eu/2016/09/27/aurelia-tester-ses-custom-elements/</link><guid isPermaLink="false">f3b42d73-e134-451f-8b55-b9c5c643906f</guid><category><![CDATA[aurelia.io]]></category><category><![CDATA[tests]]></category><category><![CDATA[unit-tests]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Tue, 27 Sep 2016 17:26:04 GMT</pubDate><content:encoded><![CDATA[<p>Tester un service est assez simple. Il suffit de l'importer et de l'instancier en lui passant des mocks de dépendances. Par contre, pour les Custom Elements, c'est plus compliqué car il faut le faire passer à travers le moteur de Aurelia. Pour cela, nous avons donc le module <code>aurelia-testing</code>, inclus par défaut dans un projet créé avec <code>aurelia-cli</code>. Voyons comment cela fonctionne.</p>

<p>(Attention, si vous voulez aussi utiliser async/await dans vos tests, il vous faudra modifier <code>tests/unit/setup.js</code> pour y importer <code>regenerator-runtime</code>. Mais vous le savez normalement si vous utilisez déjà async/await dans votre app)</p>

<h2 id="dcouverte">Découverte</h2>

<p>Prenons un custom element tout simple :</p>

<p><em>src/my-element.js</em></p>

<pre><code class="language-[language-javascript]">export class MyElement {}  
</code></pre>

<p><em>src/my-element.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;p&gt;Hello World&lt;/p&gt;  
&lt;/template&gt;  
</code></pre>

<p>Voici le test :</p>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView('&lt;my-element&gt;&lt;my-element&gt;');
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should render template', async done =&gt; {
    await component.create(bootstrap);
    const element = document.querySelector('my-element');
    expect(element.innerText.trim()).toBe('Hello World');
  });
});
</code></pre>

<p><code>StageComponent</code> va donc nous servir à rendre le composant dans le navigateur après l'avoir configuré comme on en a besoin. Ici, notre composant est très simple et n'a pas de configuration particulière. Nous nous contentons de vérifier que l'élément dans le DOM contient bien le texte spécifié dans le template.</p>

<p>Maintenant, essayons avec des données dynamiques</p>

<h2 id="attributs">Attributs</h2>

<p><em>src/my-element.js</em></p>

<pre><code class="language-[language-javascript]">import {bindable} from 'aurelia-framework';  
export class MyElement {  
  @bindable foo;
}
</code></pre>

<p><em>src/my-element.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;p&gt;${foo}&lt;/p&gt;  
&lt;/template&gt;  
</code></pre>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element
          foo.bind="foo"&gt;&lt;my-element&gt;
      `)
      .boundTo({
        foo: 'foo'
      });
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should render foo', async done =&gt; {
    await component.create(bootstrap);
    const element = document.querySelector('my-element');
    expect(element.innerText.trim()).toBe('foo');
  });
});
</code></pre>

<p>Si on veut pouvoir modifier la valeur des propriétés, il va falloir passer en mode manuel grâce à <code>manuallyHandleLifecycle</code> et appeler explicitement chaque étape du cycle de vie du composant:</p>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element
          foo.bind="foo"&gt;&lt;my-element&gt;
      `)
      .boundTo({
        foo: 'foo'
      });
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should render foo and changes value', async done =&gt; {
    await component.manuallyHandleLifecycle().create(bootstrap);
    await component.bind();
    await component.attached();
    const element = document.querySelector('my-element');
    expect(element.innerText.trim()).toBe('foo');

    await component.bind({
      foo: 'bar'
    });
    expect(element.innerText.trim()).toBe('bar');
  });
});
</code></pre>

<p>On peut aussi lire des propriétés du viewModel ou appeler des méthodes qui lui sont attachées via <code>component.viewModel</code> :</p>

<p><em>src/my-element.js</em></p>

<pre><code class="language-[language-javascript]">import {bindable} from 'aurelia-framework';  
export class MyElement {  
  @bindable onSelect;

  select(item) {
    if (typeof this.onSelect === 'function') {
      this.onSelect(item)
    }
  }
}
</code></pre>

<p><em>src/my-element.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;ul&gt;  
  &lt;li
    click.delegate="select(1)"&gt;1&lt;/li&gt;
  &lt;li
    click.delegate="select(2)"&gt;2&lt;/li&gt;
  &lt;li
    click.delegate="select(3)"&gt;3&lt;/li&gt;
&lt;/ul&gt;  
&lt;/template&gt;  
</code></pre>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element
          on-select.bind="select"&gt;&lt;my-element&gt;
      `);
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should select an item', async done =&gt; {
    let expected;
    component.boundTo({
      select(e) {
        expected = e;
      }
    })
    await component.create(bootstrap);
    component.viewModel.select(42);
    expect(expected).toBe(42);
  });
});
</code></pre>

<p>Mais dans ce cas là, on peut aussi vouloir tester si la fonction est correctement exécutée lors du click sur le bon élément. On va donc rédiger notre test ainsi :</p>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element
          on-select.bind="select"&gt;&lt;my-element&gt;
      `);
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should select an item on click', async done =&gt; {
    let expected;
    component.boundTo({
      select(e) {
        expected = e;
      }
    })
    await component.create(bootstrap);
    const lis = component.element.querySelectorAll('li');
    lis[0].click();
    expect(expected).toBe(1);
    lis[1].click();
    expect(expected).toBe(2);
    lis[2].click();
    expect(expected).toBe(3);
  });
});
</code></pre>

<h2 id="slot">Slot</h2>

<p>On peut de la même façon vérifier que les slots sont correctement remplis :</p>

<p><em>src/my-element.js</em></p>

<pre><code class="language-[language-javascript]">export class MyElement {}  
</code></pre>

<p><em>src/my-element.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;header  
  class="header"&gt;
  &lt;slot
    name="header"&gt;&lt;/slot&gt;
&lt;/header&gt;  
&lt;div  
  class="content"&gt;
  &lt;slot
    name="content"&gt;&lt;/slot&gt;
&lt;/div&gt;  
&lt;/template&gt;  
</code></pre>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element&gt;
          &lt;h1
            slot="header"&gt;Title&lt;/h1&gt;
          &lt;p
            slot="content"&gt;Hello World"&lt;/p&gt;
        &lt;my-element&gt;
      `);
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should render title and content slots', async done =&gt; {
    await component.create(bootstrap);
    const element = document.querySelector('my-element');
    expect(element.querySelector('.header').innerText.trim()).toBe('Title');
    expect(element.querySelector('.content').innerText.trim()).toBe('Hello World');
  });
});
</code></pre>

<h2 id="configuration">Configuration</h2>

<p>Il est parfois nécessaire de configurer son environnement différemment pour un test donné. Pour instancier un plugin nécessaire au fonctionnement du custom element, ou pour enregistrer un mock de dépendance dans l'injecteur. Nous allons gérer tout ça grâce à la méthode <code>configure</code> de <code>StageComponent</code> :</p>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element') // Path related to src
      .inView(`
        &lt;my-element
          on-select.bind="select"&gt;&lt;my-element&gt;
      `);
    component.configure = aurelia =&gt; {
      aurelia.use
        .standardConfiguration()
        .plugin('aurelia-property-injection');
    };
  });
});
</code></pre>

<p>Ici, notre custom element fait usage de la lib <code>aurelia-property-injection</code> et il faut donc le charger au préalable.</p>

<p>Si on a besoin de remplacer une dépendance par un mock, on peut faire ça aussi dans la configuration : <br>
<em>src/my-element.js</em></p>

<pre><code class="language-[language-javascript]">import {inject} from 'aurelia-framework';  
import {MyService} from './my-service';

@inject(MyService)
export class MyElement {  
  constructor(myService) {
    this.service = myService;
  }

  get something() {
    return this.service.getSomething();
  }
}
</code></pre>

<p><em>src/my-element.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;p&gt;${something}&lt;/p&gt;  
&lt;/template&gt;  
</code></pre>

<p><em>tests/my-element.spec.js</em></p>

<pre><code class="language-[language-javascript]">import {StageComponent} from 'aurelia-testing';  
import {bootstrap} from 'aurelia-bootstrapper';

describe('MyElement', () =&gt; {  
  let component;
  const MyServiceMocked = {
    getSomething: jasmine.createSpy('getSomething').and.returnValue('something')
  };

  beforeEach(() =&gt; {
    component = StageComponent
      .withResources('my-element')
      .inView(`
        &lt;my-element&gt;&lt;my-element&gt;
      `);
    component.configure = aurelia =&gt; {
      aurelia.container.registerInstance('MyService', MyServiceMocked);
    };
  });

  afterEach(() =&gt; {
    component.dispose();
  });

  it('should render something', async done =&gt; {
    await component.create(bootstrap);
    expect(document.querySelector('p').innerText.trim()).toBe('something');
    done();
  });
});
</code></pre>

<p>Tout cela suffit déjà à tester la grande majorité des cas de figure. Nous verrons dans un prochain article comment teste les custom attributes.</p>]]></content:encoded></item><item><title><![CDATA[WebComponent avec VueJS]]></title><description><![CDATA[<p>Aujourd'hui, j'ai testé <a href="http://vuejs.org">VueJS</a>. Cette lib permet de créer assez simplement des webcomponent réactif, voire des apps complètes. Mais ce que j'ai apprécié, c'est justement cette possibilité de créer des petits webcomponents autonomes qu'on peut ensuite réutiliser n'importe où. Pour prouver mes dires, j'ai écrit un formulaire de recherche qui</p>]]></description><link>http://blog.hadrien.eu/2016/09/24/webcomponent-avec-vuejs/</link><guid isPermaLink="false">afd856f6-d451-44b5-88b6-a695530f9ea3</guid><category><![CDATA[vuejs]]></category><category><![CDATA[framework]]></category><category><![CDATA[Javascript]]></category><category><![CDATA[ES6]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Sat, 24 Sep 2016 21:00:37 GMT</pubDate><content:encoded><![CDATA[<p>Aujourd'hui, j'ai testé <a href="http://vuejs.org">VueJS</a>. Cette lib permet de créer assez simplement des webcomponent réactif, voire des apps complètes. Mais ce que j'ai apprécié, c'est justement cette possibilité de créer des petits webcomponents autonomes qu'on peut ensuite réutiliser n'importe où. Pour prouver mes dires, j'ai écrit un formulaire de recherche qui utilise <a href="https://www.algolia.com">Algolia</a> pour chercher dans les posts de ce blog. Pour l'afficher ci dessous, je n'ai eu qu'à insérer une balise <code>link</code>, une balise <code>script</code> puis la balise correspondant à mon webcomponent : <code>&lt;search&gt;</code> :</p>

<pre><code class="language-[language-markup]">&lt;search&gt;&lt;/search&gt;  
&lt;link href=https://works.hadrien.eu/algolia-vuejs/css/app.2c00791366d54647d6eb51ac93e64cec.css rel=stylesheet&gt;  
&lt;script type=text/javascript src=https://works.hadrien.eu/algolia-vuejs/js/search-component.js&gt;&lt;/script&gt;  
</code></pre>

<p>Et voilà le résultat :</p>

<div style="
    max-height: 500px;
    overflow: auto;
  ">
  <search></search>
</div>  

<p><link href="https://works.hadrien.eu/algolia-vuejs/css/app.2c00791366d54647d6eb51ac93e64cec.css" rel="stylesheet">  </p>

<script type="text/javascript" src="https://works.hadrien.eu/algolia-vuejs/js/search-component.js"></script>

<p>Je peux donc facilement insérer mon formulaire de recherche partout où j'en ai envie que ça soit sur une page html statique, un article de blog ou une application complexe angularjs, aurelia ou n'importe quoi d'autre.</p>

<p>Ce que j'ai beaucoup apprécié avec cet outil, c'est qu'il permet de séparer template, logique et style à la façon des web components.</p>

<p>La partie template utilise le moteur de template mustache et rassurera tous les développeurs angular. On peut interpoler des expression, appeler des méthodes, utiliser des filtres, insérer d'autres components, faire du two way data binding.</p>

<p>Coté implémentation, c'est du ESNext. Même si la déclaration du component est un simple objet et pas une classe, on peut facilement importer des services ou autres libs grâce à <code>import from</code>. Le component dispose d'un cycle de vie complet grâce auquel on pourra exécuter du code lors de la création de la balise, ou lors de sa suppression par exemple.</p>

<p>Pour finir, on peut donc définir un style pour le component et grâce à un attribut particulier (<code>scoped</code>), on peut  limiter ces déclarations css à ce composant uniquement.</p>

<p>Voici pour illustrer tout ça mon component <code>&lt;search-form&gt;</code> inséré lui même par le component principal <code>&lt;search&gt;</code> et définie dans le fichier <code>SearchForm.vue</code> :  </p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
  &lt;form&gt;
    &lt;input
      type="search"
      placeholder="Search something"
      v-model="query"
      debounce="500"&gt;
    &lt;p&gt;Powered by &lt;span&gt;Algolia&lt;/span&gt;&lt;/p&gt;
  &lt;/form&gt;
&lt;/template&gt;

&lt;script&gt;  
import search from '../services/search'  
export default {  
  data () {
    return {
      query: ''
    }
  },
  watch: {
    query: async function (newVal) {
      await search.search(newVal)
    }
  }
}
&lt;/script&gt;

&lt;!-- Add "scoped" attribute to limit CSS to this component only --&gt;  
&lt;style scoped&gt;  
input {  
  font-size: 40px;
  line-height: 40px;
}
p {  
  font-size: 14px;
}
span {  
  display: inline-block;
  width: 50px;
  height: 16px;
  background: url(../assets/Algolia_logo_bg-white.svg) no-repeat;
  background-size: contain;
  text-indent: -10000px;
  vertical-align: middle;
}
&lt;/style&gt;
</code></pre>

<p>Et au passage, j'ai donc aussi testé <a href="https://www.algolia.com">Algolia</a> qui est un moteur de recherche en Saas permettant d'externaliser la recherche de données de façon très simple mais surtout très performante. Très intéressant service !</p>]]></content:encoded></item><item><title><![CDATA[Une startup pour créer son poste]]></title><description><![CDATA[<p>En novembre dernier, j'ai été <strong>licencié économique</strong> suite à la liquidation de mon employeur. Cette situation <del>a</del><a href="http://blog.hadrien.eu/2016/09/09/une-startup-pour-creer-son-poste/#ref-1"><sup>1</sup></a> avait des avantages pour aider un individu à monter sa société. En effet, après un licenciement économique, le salarié peut accepter un CSP : <strong>Contrat de Sécurisation Professionnelle</strong>. Ça veut dire qu'il n'est</p>]]></description><link>http://blog.hadrien.eu/2016/09/09/une-startup-pour-creer-son-poste/</link><guid isPermaLink="false">16c846e7-ea5d-4497-99f8-1d6225688548</guid><category><![CDATA[work]]></category><category><![CDATA[télé-travail]]></category><category><![CDATA[chômage]]></category><category><![CDATA[CSP]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Fri, 09 Sep 2016 14:59:20 GMT</pubDate><content:encoded><![CDATA[<p>En novembre dernier, j'ai été <strong>licencié économique</strong> suite à la liquidation de mon employeur. Cette situation <del>a</del><a href="http://blog.hadrien.eu/2016/09/09/une-startup-pour-creer-son-poste/#ref-1"><sup>1</sup></a> avait des avantages pour aider un individu à monter sa société. En effet, après un licenciement économique, le salarié peut accepter un CSP : <strong>Contrat de Sécurisation Professionnelle</strong>. Ça veut dire qu'il n'est pas <em>chômeur</em> mais <em>stagiaire de la formation professionnelle</em> (c'est bon pour les stats ça) et qu'il touche une indemnité plus élevée pendant un an : 100% de son précédent salaire au lieu de la moitié. Ainsi, un salarié licencié éco a un an pour créer un projet et monter une société sereinement (ou se former, ou chercher un poste à quatre feuilles, ou aller à la pêche, c'est selon).</p>

<p>Dans mon cas, j'ai décidé de créer mon propre emploi. C'est ainsi que j'ai participé au projet <a href="http://blog.hadrien.eu/tag/reverb/">Reverb</a> avec deux objectifs en vue :</p>

<ul>
<li><strong>créer mon poste</strong> de CTO dans une startup dont j'aurais été le co-fondateur,</li>
<li><strong>me former</strong> dans les technologies que j'avais envie d'utiliser dans mon nouveau poste.</li>
</ul>

<p>Comme vous l'avez probablement constaté, le projet de création d'entreprise est tombé à l'eau à cause de divergence idéologiques. En effet, mon ex-futur-associé qui partageait le même objectif de création de poste au départ a finalement préféré juste devenir riche, et tant qu'à faire, tout seul. Je l'ai donc laissé jouer dans son coin, et j'ai fermé <a href="https://reverb.re">Reverb</a> pour les raisons expliquées sur la page d'accueil. Et j'ai continué ma route de mon côté.</p>

<p>Alors à ce moment on se dit que ces six mois de travail ont du être une vraie perte de temps ! J'aurais pu faire plein de choses pendant ces 6 mois :</p>

<ul>
<li>suivre des formations pole emploi,</li>
<li>faire du freelance au black,</li>
<li>trouver le premier poste venu dès mon premier mois de CSP et toucher une grosse prime de reclassement,</li>
<li>aller à la pêche…</li>
</ul>

<p>Sauf qu'en fait, j'ai fait bien mieux. Je me suis auto formé dans de nouvelles technos (Aurelia notamment) et des technos que je ne maitrisais pas du tout (cloud hosting, adminsys). Ces six mois ont été équivalent à plusieurs milliers d'euros de formations. <br>
En plus de ça, comme j'avais du travail avec Reverb, ça m'a donné le courage de refuser des offres de postes qui ne me paraissaient pas assez intéressantes à mes yeux et que j'aurais regretté d'avoir accepté (des SSII, des postes pas en télétravail ou pas assez payé, des projets <del>ne correspondant pas à mon éthique </del> de merde).</p>

<p>Et au final, grâce à ces nouvelles compétences acquises, le poste que je voulais me créer, c'est une autre startup qui l'a créé et qui me l'a confié. Le but a été atteint par une autre route. Je fais maintenant du nodejs, de l'aurelia, du hosting, le tout, en télétravail. Exactement ce que j'aurais fait si j'avais monté Reverb mais sans m'embarrasser du coté chiant qu'est la création d'entreprise.</p>

<p>Donc, pour ceux qui sont en CSP et ceux qui y seront, profitez bien de cette situation et ne glandez pas. Créez votre poste.</p>

<div id="ref-1">
[<sup>1</sup>]: Depuis la loi El Khomri, pour avoir droit au CSP, il faut avoir deux ans d'ancienneté et la durée d'indemnisation ne dure que six mois.
</div>]]></content:encoded></item><item><title><![CDATA[Intégration continue avec Circleci et Amazon]]></title><description><![CDATA[<p>Dans ma nouvelle équipe, on utilise les services d'hébergement d'<a href="https://aws.amazon.com/fr/">Amazon</a> et l'outil d'intégration continue <a href="https://circleci.com/">CircleCI</a>. J'ai donc du adapter les différents projets que j'ai architecturé pour les plier à ces exigences. Cela n'a pas été une mince affaire, mais finalement, non seulement ça s'est fait assez rapidement, mais surtout,</p>]]></description><link>http://blog.hadrien.eu/2016/08/23/integration-continue-avec-circleci-et-amazon/</link><guid isPermaLink="false">4dc18362-a93e-4175-945c-fd3eab26395e</guid><category><![CDATA[CI]]></category><category><![CDATA[architecture]]></category><category><![CDATA[aurelia.io]]></category><category><![CDATA[Amazon]]></category><category><![CDATA[Github]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Tue, 23 Aug 2016 13:38:00 GMT</pubDate><content:encoded><![CDATA[<p>Dans ma nouvelle équipe, on utilise les services d'hébergement d'<a href="https://aws.amazon.com/fr/">Amazon</a> et l'outil d'intégration continue <a href="https://circleci.com/">CircleCI</a>. J'ai donc du adapter les différents projets que j'ai architecturé pour les plier à ces exigences. Cela n'a pas été une mince affaire, mais finalement, non seulement ça s'est fait assez rapidement, mais surtout, ça marche du feu de dieu !</p>

<h2 id="infrastructure">Infrastructure</h2>

<p>Les applications consistent en des SPA <a href="http://aurelia.io/">Aurelia</a> tapant une API en CORS. L'une des deux apps a son propre backend en nodejs.</p>

<p>Les clients javascript sont donc hébergés sur Cloudfront/S3. Une tâche a été rajouté au projet pour être lancée avec aurelia-cli qui va créer un nouveau dossier dans le bucket S3, y uploader les fichiers, puis modifier la conf de cloudfront pour faire pointer le root sur ce nouveau dossier et invalider le cache. Cela permet de faire facilement un rollback en cas de problème. Seul soucis : la mise à jour effective prend beaucoup de temps car Amazon prend entre 5 et 10mn à redémarrer le service et à invalider le cache après une mise à jour. Mais gros avantage : ça sert à toute vitesse ! Et on peut créer autant de configuration Cloudfront que d'environnements nécéssaire, en l'occurence une prod et un staging.</p>

<p>Le backend est servi par ElasticBeanstalk. Là aussi, c'est très facile une fois que tout est correctement configuré : on déploie très simplement avec le cli fourni par Amazon. On peut ici aussi facilement cibler l'environnement de son choix : prod et staging.</p>

<h2 id="intgrationcontinue">Intégration continue</h2>

<p>CircleCI est un outil intégré avec Github gratuit tant qu'on reste dans des limites très facilement supportable : une build à la fois. Il permet de lancer la construction d'un projet et de lancer ses tests, récupérer éventuellement des fichiers comme le rapport de couverture de code, puis finalement déployer. Tout se configure grâce à un fichier circle.yml dans lequel on décrira la liste des tâches à effectuer, les commandes à lancer avant ou après l'installation, les tests, le déploiement, etc. Et surtout, on peut définir dans quel environnement déployer selon la nature de la construction, par exemple, s'il s'agit de la branche master, on déploie sur staging, mais s'il s'agit d'un tag, on déploie sur la prod. <br>
On en oublie complètement la tâche lourdingue du déploiment : dès qu'une pull request est fusionnée dans master, elle est en staging quelques minutes après et quand on veut envoyer en prod, on se contente de pousser un nouveau tag.</p>

<h3 id="exempledeconfigurationcircleyml">Exemple de configuration circle.yml</h3>

<pre><code>machine:
  node:
    version: stable
dependencies:
  pre:
    - npm install -g aurelia-cli
deployment:
  production:
    tag: /.*/
    commands:
      - au i18n load
      - au deploy --env prod
  staging:
    branch: master
    commands:
      - au i18n load
      - au deploy --env stage
</code></pre>]]></content:encoded></item><item><title><![CDATA[Aurelia : JSPM vs AU]]></title><description><![CDATA[<p>Pour convaincre ma nouvelle équipe que <strong>Aurelia est le meilleur framework du moment</strong>, il a fallu que je leur prouve sur tous les plans. Je savais déjà qu'en terme de facilité de développement, il était déjà bien devant les autres, mais en terme de performance, je n'étais pas bien sur.</p>]]></description><link>http://blog.hadrien.eu/2016/08/08/aurelia-jspm-vs-au/</link><guid isPermaLink="false">6254602b-a6ee-44ab-ad65-26f1abc38b0c</guid><category><![CDATA[aurelia.io]]></category><category><![CDATA[jspm]]></category><category><![CDATA[ES6]]></category><category><![CDATA[performances]]></category><category><![CDATA[requirejs]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 08 Aug 2016 19:25:55 GMT</pubDate><content:encoded><![CDATA[<p>Pour convaincre ma nouvelle équipe que <strong>Aurelia est le meilleur framework du moment</strong>, il a fallu que je leur prouve sur tous les plans. Je savais déjà qu'en terme de facilité de développement, il était déjà bien devant les autres, mais en terme de performance, je n'étais pas bien sur. Et en effet, il y avait bel et bien un soucis de lenteur sur mes applications conçues avec l'outil qui a été la recommandation depuis le lancement de la pré-alpha : JSPM.</p>

<p><a href="https://blog.hadrien.eu/2015/02/05/jspm-des-modules-a-la-mode-es6/">JSPM</a> est un outil assez récent visant à nous permettre de coder en ES6 sans avoir à se soucier de l'origine des modules. C'était un outil très intéressant sur le papier puisque même Angular JS 2 était basé dessus. Malheureusement, le projet a pris beaucoup de retard et a laissé trainer de graves soucis de performances. Il m'a ainsi été conseillé par Rob Eisenberg de passer à Webpack qui avait vraiment bien évolué ou à Aurelia-cli. Ce que je fis juste après avoir appris la nouvelle de <a href="http://blog.durandal.io/2016/07/27/aurelia-1-0-is-here/">la sortie de la version 1.0 d'Aurelia</a>.</p>

<p>Et bien c'est en effet impressionnant. Avec JSPM, il fallait attendre entre 2 et 3 secondes pour que l'application soit chargée et utilisable. Pourtant l'app était toute petite, mais JSPM avait besoin d'une seconde pour exécuter pas mal de choses avant de charger l'application. Avec aurelia-cli, qui utilise requirejs, <strong>une seconde suffit</strong>. On sent vraiment la différence, vous pourrez vous en rendre compte avec la vidéo suivante :</p>

<iframe src="https://www.youtube.com/embed/WZFN7hTkQrM" frameborder="0" allowfullscreen></iframe>

<p>Alors si vous voulez essayer Aurelia, je vous invite à passer immédiatement par aurelia-cli :</p>

<pre><code># npm install -g aurelia-cli
$ au new
</code></pre>

<p>vous proposera de créer un nouveau projet en choisissant tous les outils de votre préférence : ESNext ou Typescript, précompilateur css, intégration dans votre éditeur préféré… Puis vous pourrez lancer votre app avec :</p>

<pre><code>$ au run --watch
</code></pre>

<p>Et vous pourrez même créer de nouveaux scripts en ESNext et les lancer d'un coup de :</p>

<pre><code>$ au my-custom-command
</code></pre>

<p>Et vous la deployrez après l'avoir compilée d'un :</p>

<pre><code>$ au build --env prod
</code></pre>]]></content:encoded></item><item><title><![CDATA[Déployer son application javascript]]></title><description><![CDATA[<p>Pour déployer une application, il existe pas mal de solutions, mais celle que j'ai trouvé la plus pratique est <a href="http://capistranorb.com/#">Capistrano</a>. Seul soucis, c'est du ruby, et quand on est dev fullstack javascript et qu'on veut déployer une app node.js ou simplement une SPA, ça la fout mal de devoir</p>]]></description><link>http://blog.hadrien.eu/2016/07/05/deployer-son-application-front/</link><guid isPermaLink="false">17ff78fd-5368-4b9a-b089-058e647244cc</guid><category><![CDATA[Javascript]]></category><category><![CDATA[architecture]]></category><category><![CDATA[deploy]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Tue, 05 Jul 2016 10:39:41 GMT</pubDate><media:content url="https://cloud.githubusercontent.com/assets/266302/3756454/81df9f46-182e-11e4-9da6-b2c7a6b84136.png" medium="image"/><content:encoded><![CDATA[<img src="https://cloud.githubusercontent.com/assets/266302/3756454/81df9f46-182e-11e4-9da6-b2c7a6b84136.png" alt="Déployer son application javascript"><p>Pour déployer une application, il existe pas mal de solutions, mais celle que j'ai trouvé la plus pratique est <a href="http://capistranorb.com/#">Capistrano</a>. Seul soucis, c'est du ruby, et quand on est dev fullstack javascript et qu'on veut déployer une app node.js ou simplement une SPA, ça la fout mal de devoir se coltiner du ruby. Heureusement, il existe une alternative en javascript qui offre le même principe de fonctionnement : <a href="https://github.com/shipitjs/shipit">ShipitJS</a>.</p>

<p>Cet outil va vous permettre de cloner votre dépôt git dans le dossier local de votre choix, d'effectuer des tâches puis enfin d'envoyer sur votre ou vos serveurs les fichiers de votre choix, avec la possibilité de rollback à n'importe quel moment. On pourra alors utiliser les commandes :</p>

<pre><code>shipit &lt;env&gt; deploy
</code></pre>

<p>et</p>

<pre><code>shipit &lt;env&gt; rollback
</code></pre>

<p>Pour deployer sur l'environnement configuré, puis pour revenir à la version précédente. On pourra créer autant d'environnements que nécessaire.</p>

<p>Alors comment ça marche ?</p>

<h2 id="installation">Installation</h2>

<p>On va commencer par installer shipit-cli et shipit-npm. Le premier étant la commande à utiliser et le second, une liste de tâches qui vont s'ajouter à <code>shipit-deploy</code>(inclus dans <code>shipit-cli</code>) et qui permettront de lancer automatiquement <code>npm install</code>. Mais nous le verrons plus loin en détail.</p>

<pre><code>$ npm install --save-dev shipit-cli shipit-npm
</code></pre>

<p>Vous pouvez bien entendu installer shipit-cli en global, mais c'est moche, <a href="https://blog.hadrien.eu/2015/10/13/global-is-evil/">je préfère le garder en local</a> et utiliser <code>npm run</code> pour lancer la commande. Nous le verrons par la suite.</p>

<h2 id="configuration">Configuration</h2>

<p>Il nous faut maintenant un fichier <code>shipitfile.js</code> à la racine de votre projet dans lequel nous allons configurer ce qui doit être fait.</p>

<pre><code class="language-[language-javascript]">module.exports = function (shipit) {  
  require('shipit-deploy')(shipit);
  require('shipit-npm')(shipit);

  shipit.initConfig({
    default: {
      repositoryUrl: 'git@github.com:you/yourproject.git',
      workspace: '/tmp/your-project',
      deployTo: '/home/deploy/your-project',
      deleteOnRollback: false,
      keepReleases: 2,
      npm: {
        remote: false
      }
    },
    staging: {
      servers: 'deploy@192.168.33.42',
      branch: 'feature/deploy'
    },
    production: {
      servers: 'deploy@your-project.com',
      branch: 'master'
    }
  });
};
</code></pre>

<p>Nous commençons par charger <code>shipit-deploy</code> qui est une liste de tâches pré-configurées pour permettre un fonctionnement à la Capistrano : le dépôt git est d'abord cloné dans un dossier temporaire, puis la branche spécifiée est sélectionnée, puis l'ensemble du dossier est copié sur le serveur distant en gardant les précédentes releases. Le dossier <code>deployTo</code> contient un dossier <code>releases</code> ainsi qu'un lien symbolique nommé <code>current</code> qui pointe vers le dossier dans <code>releases</code> correspondant à la version courante. On peut ainsi spécifier combien de versions précédentes on souhaite garder et facilement revenir à la version précédente en créant un autre lien symbolique.</p>

<p>On charge ensuite <code>shipit-npm</code> qui va se charger de lancer la commande <code>npm install</code> au bon moment.</p>

<p>Il ne reste qu'à configurer les différents paramètres que vous retrouverez <a href="https://github.com/shipitjs/shipit">dans la doc</a> mais dont les principaux sont les suivants :</p>

<ul>
<li><code>repositoryUrl</code> : indique l'url du dépôt à déployer</li>
<li><code>workspace</code> : emplacement du dossier de travail, attention à ne surtout pas indiquer le dossier courant car la première étape est de tout y supprimer. Privilégiez un dossier dans <code>/tmp</code>.</li>
<li><code>deployTo</code>: le chemin sur le serveur distant où copier les fichiers.</li>
<li><code>deleteOnRollback</code> : est-ce-qu'on supprime la version la plus récente après un rollback ?</li>
<li><code>keepReleases</code> : le nombre de versions qu'on garde</li>
</ul>

<p>Ensuite, on peut surcharger ces paramètres selon les environnements. On va d'abord y spécifier le ou les serveurs où deployer et on peut aussi indiquer la branche à sélectionner. Par défaut, c'est master, mais on peut en choisir une autre pour le staging par exemple.</p>

<p>Une fois tout ça fait, il faut encore configurer son serveur. Tout ce dont il a besoin, c'est d'un user <code>deploy</code> (ou ce que vous voulez finalement, puisque vous pouvez le spécifier au niveau du host) et d'un accès ssh avec clé publique. On met ça en place très facilement :</p>

<pre><code># useradd deploy -m  

# mkdir /home/deploy/.ssh  
# cat my_id_rsa.pub &gt;&gt; /home/deploy/.ssh/authorized_keys  
# chown -R deploy:deploy /home/deploy/.ssh  
# chmod 700 /home/deploy/.ssh  
# chmod 600 /home/deploy/.ssh/authorized_keys
</code></pre>

<p>Si vous pouvez vous connecter d'un simple <code>ssh deploy@myhost.com</code> alors c'est bon.</p>

<p>Et là, vous pouvez deployer d'un simple :</p>

<pre><code>$ ./node_modules/.bin/shipit staging deploy
</code></pre>

<p>Ou mieux :</p>

<pre><code>$ npm run deploy-staging
</code></pre>

<p>Après avoir ajouté à <code>package.json</code> les scripts suivant :</p>

<pre><code class="language-[language-javascript]">"scripts": {
    // […]
  "deploy-staging": "shipit staging deploy",
  "rollback-staging": "shipit staging rollback",
  "deploy-production": "shipit production deploy",
  "rollback-production": "shipit production rollback"
}
</code></pre>

<h1 id="tchespersonnalises">Tâches personnalisées</h1>

<p>Mais il manque encore quelque chose !! Deux cas de figures : le premier : vous avez besoin de lancer une commande de compilation avant d'envoyer sur le serveur distant. Typiquement un <code>gulp export</code> par exemple qui va se charger de générer la version combinée et optimisée de votre application dans un dossier non versionné. Le second cas, c'est quand vous avez besoin de lancer une commande sur votre serveur distant après avoir envoyé les fichier, par exemple un petit <code>forever restart myproject</code>.</p>

<p>Dans les deux cas, il faut d'abord créer une tâche, puis écouter <a href="https://github.com/shipitjs/shipit-deploy#workflow-tasks">l'évènement correspondant</a> au moment où on veut lancer cette tâche. Il existe deux types de taches : les taches simples et les tâches bloquantes. Le second type permet d'attendre la fin de celle-ci avant de continuer la liste de tâches. On va donc utiliser soit <code>shipit.task(name, taskFn)</code> soit <code>shipit.blTask(name, taskFn)</code>.</p>

<h2 id="lancerunecommandeenlocal">Lancer une commande en local</h2>

<p>On va partir de l'exemple de la tâche <code>gulp export</code> qui génère un dossier <code>export</code> avec la version compilée de l'application. On veut donc lancer cette commande après avoir chargé le projet dans l'espace de travail local. Il s'agit donc de l'évènement <code>fetched</code>. Puisqu'on a chargé <code>shipit-npm</code>, notre tâche se fera après la tâche <code>npm install</code>. Ça tombe bien. Voici notre tâche :</p>

<pre><code class="language-[language-javascript]">shipit.blTask('jspm:install', () =&gt; {  
  return shipit.local(`cd ${shipit.config.workspace} &amp;&amp; ./node_modules/.bin/jspm install`);
});
shipit.blTask('gulp:deploy', () =&gt; {  
  return shipit.local(`cd ${shipit.config.workspace} &amp;&amp; ./node_modules/.bin/gulp deploy`);
});
</code></pre>

<p><code>shipit.local</code> permet donc d'exécuter une commande sur la machine locale. Par défaut, nous sommes situé à l'emplacement où le script <code>shipit staging deploy</code> a été lancé, c'est pourquoi il faut commencer par se déplacer dans l'espace de travail.</p>

<p>Ensuite, on attache notre tâche à l'évenement concerné :</p>

<pre><code class="language-[language-javascript]">shipit.on('fetched', () =&gt; shipit.start(['jspm:install', 'gulp:deploy']));  
</code></pre>

<p>Dans la cas d'un client SPA, sans backend, on n'a que le dossier compilé à envoyer, on peut donc utiliser l'attribut <code>dirToCopy</code> dans la configuration de shipit pour spécifier le dossier en question au lieu d'envoyer le dépôt entier.</p>

<h2 id="lancerunecommandedistance">Lancer une commande à distance</h2>

<p>Maintenant que nous avons envoyé notre code sur la machine distante, nous voudrions pouvoir redémarrer le serveur node afin qu'il fonctionne avec la dernière version du code. On va prendre comme exemple l'utilisation de <code>forever</code> qui propose la commande <code>forever restart &lt;name&gt;</code> afin de facilement redémarrer une application node. Nous allons donc créer la tâche suivante :</p>

<pre><code class="language-[language-javascript]">shipit.blTask('forever:restart', () =&gt; {  
  return shipit.remote('forever restart myproject');
});
shipit.on('published', () =&gt; shipit.start('forever:restart'));  
</code></pre>

<p>Et c'est parti pour du deploy à volonté sur n'importe quel serveur avec accès ssh. Il est même possible de deployer <a href="https://github.com/KrashStudio/shipit-aws">sur du AWS</a> ou de <a href="https://github.com/timkelty/shipit-db">sauvegarder ses bases de données</a>.</p>]]></content:encoded></item><item><title><![CDATA[Micro moteur de template]]></title><description><![CDATA[<p>Je travaille à l'intégration d'un site de e-commerce en ce moment, qui contient quelques composants en javascript. Comme je ne m'occupe que de l'intégration, je n'écrit que du javascript vanilla, sans librairies qui aurait pu m'être utiles ici comme React ou Polymer. Sauf que je suis arrivé à un point</p>]]></description><link>http://blog.hadrien.eu/2016/06/21/micro-moteur-de-template/</link><guid isPermaLink="false">a696e627-155c-4632-bec5-df73853028ca</guid><category><![CDATA[experiment]]></category><category><![CDATA[ES6]]></category><category><![CDATA[template]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Tue, 21 Jun 2016 13:48:52 GMT</pubDate><content:encoded><![CDATA[<p>Je travaille à l'intégration d'un site de e-commerce en ce moment, qui contient quelques composants en javascript. Comme je ne m'occupe que de l'intégration, je n'écrit que du javascript vanilla, sans librairies qui aurait pu m'être utiles ici comme React ou Polymer. Sauf que je suis arrivé à un point où un composant générait une vue en javascript. Grâce aux templates literal, le template reste lisible dans le javascript, mais j'étais quand même inquiet quand à la maintenabilité de cette partie. J'aurais préféré que ce template reste là où est sa place : dans un fichier html. Alors j'ai bidouillé une classe de templating à coup de vilain eval, et ça a marché.</p>

<p>Donc au départ, nous avions ceci :</p>

<pre><code class="language-[language-javascript]">class MyComponent {  
  function generateElement () {
    const tags = ['foo', 'bar', 'babar', 'lelefan'];
    const displayButton = true;
    const el = document.createElement('div');
    el.innerHTML = `
      &lt;div
        class="mycomponent-ctn"&gt;
        &lt;p
          class="mycomponent-text"&gt;Hello World&lt;/p&gt;
        &lt;ul&gt;
          ${tags.map(tag =&gt; `&lt;li&gt;${tag}&lt;/li&gt;`).join('')}
        &lt;/ul&gt;
        ${displayBtn ?
        `&lt;button&gt;OK&lt;/button&gt;`:
        ''}
      &lt;/div&gt;
    `;
    document.body.appendChild(el);
  }
}
</code></pre>

<p>Sauf que ce gros pavé de html perdu au milieu d'une classe javascript, ça fait tâche et on n'est pas à l'abri de tout casser en cherchant à changer une classe css. Voici ce que j'ai réussi à avoir à l'aide de mon micro template renderer :</p>

<pre><code class="language-[language-markup]">&lt;template  
  id="my-tpl"&gt;
  &lt;div
    class="mycomponent-ctn"&gt;
    &lt;p
      class="mycomponent-text"&gt;Hello World&lt;/p&gt;
    &lt;ul&gt;
      ${data.tags.map(tag =&gt; `&lt;li&gt;${tag}&lt;/li&gt;`).join('')}
    &lt;/ul&gt;
    ${data.displayBtn ?
    `&lt;button&gt;OK&lt;/button&gt;`:
    ''}
  &lt;/div&gt;
&lt;/template&gt;  
</code></pre>

<pre><code class="language-[language-javascript]">import {TemplateRenderer} from './template-renderer';

class MyComponent {  
  function generateElement () {
    const tpl = new TemplateRenderer(document.querySelector('#my-tpl'));
    document.body.appendChild(tpl.render({
      tags: ['foo', 'bar', 'babar', 'lelefan'],
      displayButton: true
    }));
  }
}
</code></pre>

<p>Et voici donc la classe magique qui fait tout le travail :</p>

<pre><code class="language-[language-javascript]">export class TemplateRenderer {  
  constructor(template) {
    this.template = template;
  }

  render(data = {}) {
    const newEl = document.createElement('div');
    newEl.innerHTML = this._interpolate(this.template.innerHTML, data);
    return newEl;
  }

  _interpolate(tpl, _data) {
    const data = _data;
    tpl = tpl
      .replace(/&amp;gt;/g, '&gt;')
      .replace(/'/g, '\'');
    try {
      return eval('`' + tpl + '`');
    } catch (e) {
      console.error(e);
      return '';
    }
  }
}
</code></pre>

<p>Oui, c'est assez vilain, ça utilise du <code>eval</code> et puis ça ne marche qu'avec les browsers qui gèrent ES6 et les <code>strings literal</code> puisque ça passe par un <code>eval</code>… Mais c'est intéressant de voir qu'on peut aujourd'hui faire du templating avec 3 lignes de code seulement.</p>]]></content:encoded></item><item><title><![CDATA[Reverb, nouvelle interface]]></title><description><![CDATA[<p>Je vous présente la nouvelle interface de <a href="https://reverb.re/">Reverb</a>.</p>

<p>Conçue avec le <a href="https://blog.hadrien.eu/tag/aurelia.io/">framework Aurelia.io</a>, j'ai pu rapidement reprendre les composants existants dans la précédente itération et ainsi me concentrer sur l'intégration. J'ai ainsi pu m'attarder sur les notions d'animations et rendre l'interface extrêmement fluide et vivante. Elle sera en production</p>]]></description><link>http://blog.hadrien.eu/2016/06/13/reverb-nouvelle-interface/</link><guid isPermaLink="false">919e4fc0-36e5-4dbb-b360-f27383d255dd</guid><category><![CDATA[aurelia.io]]></category><category><![CDATA[animation]]></category><category><![CDATA[css]]></category><category><![CDATA[Javascript]]></category><category><![CDATA[reverb]]></category><category><![CDATA[startup]]></category><category><![CDATA[produit]]></category><category><![CDATA[vidéo]]></category><category><![CDATA[démo]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 13 Jun 2016 19:43:10 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2016/06/Capture-d-e-cran-2016-06-10-09-29-46.png" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2016/06/Capture-d-e-cran-2016-06-10-09-29-46.png" alt="Reverb, nouvelle interface"><p>Je vous présente la nouvelle interface de <a href="https://reverb.re/">Reverb</a>.</p>

<p>Conçue avec le <a href="https://blog.hadrien.eu/tag/aurelia.io/">framework Aurelia.io</a>, j'ai pu rapidement reprendre les composants existants dans la précédente itération et ainsi me concentrer sur l'intégration. J'ai ainsi pu m'attarder sur les notions d'animations et rendre l'interface extrêmement fluide et vivante. Elle sera en production dès Mardi 14 Juin, si tout va bien :x</p>

<iframe width="720" height="420" src="https://www.youtube.com/embed/9VosUx4kAoU" frameborder="0" allowfullscreen></iframe>

<p>Et bien entendu, vous êtes vigoureusement invité <a href="https://reverb.re/">à essayer Reverb</a> !</p>]]></content:encoded></item><item><title><![CDATA[Des animations avec Aurelia]]></title><description><![CDATA[<p>Animer une application Aurelia est extrêmement simple. Vous pouvez en apprendre pas mal <a href="http://blog.durandal.io/2015/07/17/animating-apps-with-aurelia-part-1/">sur la théorie sur le blog de l'auteur</a> mais ici, on va voir comment rapidement mettre en place une animation sur une liste d'éléments.</p>

<iframe width="420" height="315" src="https://www.youtube.com/embed/56iCVVx8SsY" frameborder="0" allowfullscreen></iframe>

<p>La première chose à faire est d'activer le plugin <code>aurelia-animator-css</code> dans votre fichier</p>]]></description><link>http://blog.hadrien.eu/2016/05/30/des-animations-avec-aurelia/</link><guid isPermaLink="false">6302b0fa-1dc0-4d8d-be9d-f13b7e5ef1a2</guid><category><![CDATA[Javascript]]></category><category><![CDATA[aurelia.io]]></category><category><![CDATA[css]]></category><category><![CDATA[animation]]></category><dc:creator><![CDATA[Hadrien]]></dc:creator><pubDate>Mon, 30 May 2016 09:08:18 GMT</pubDate><media:content url="http://blog.hadrien.eu/content/images/2016/05/Capture-d-e-cran-2016-05-30-11-02-50.png" medium="image"/><content:encoded><![CDATA[<img src="http://blog.hadrien.eu/content/images/2016/05/Capture-d-e-cran-2016-05-30-11-02-50.png" alt="Des animations avec Aurelia"><p>Animer une application Aurelia est extrêmement simple. Vous pouvez en apprendre pas mal <a href="http://blog.durandal.io/2015/07/17/animating-apps-with-aurelia-part-1/">sur la théorie sur le blog de l'auteur</a> mais ici, on va voir comment rapidement mettre en place une animation sur une liste d'éléments.</p>

<iframe width="420" height="315" src="https://www.youtube.com/embed/56iCVVx8SsY" frameborder="0" allowfullscreen></iframe>

<p>La première chose à faire est d'activer le plugin <code>aurelia-animator-css</code> dans votre fichier main.js :</p>

<pre><code class="language-[language-javascript]">export function configure(aurelia) {  
  aurelia.use
  .standardConfiguration()
  .developmentLogging()
  .plugin('aurelia-animator-css');

  aurelia.start()
  .then(() =&gt; aurelia.setRoot());
}
</code></pre>

<p>À partir de ce moment là, Aurelia va s'intéresser aux éléments comportant la classe <code>au-animate</code> et agir en conséquence. Lorsque l'élément sortira du DOM, la classe <code>au-leave</code> lui sera ajouté, puis la classe <code>au-leave-active</code> et enfin lorque l'animation sera terminée, l'élement sera retiré. Même principe pour l'entrée d'un élément dans le DOM avec <code>au-enter</code> et <code>au-enter-active</code> mais aussi lors de l'ajout ou le retrait d'une classe à un élément. Dans ces deux derniers cas, la transition se fera à l'aide de <code>&lt;nom de la classe&gt;-add</code> et <code>&lt;nom de la classe&gt;-remove</code>.</p>

<p>Vous aurez donc compris qu'avec tout ça, tout ce qu'il reste à faire, c'est écrire la css qui va bien à notre liste.</p>

<p>Commençons par mettre en place notre liste :</p>

<p><em>view.html</em></p>

<pre><code class="language-[language-markup]">&lt;template&gt;  
&lt;ul  
  class="animated-collection au-stagger"&gt;
  &lt;li
    repeat.for="item of items"
    class="au-animate"&gt;
    &lt;span
      click.trigger="remove(item)"&gt;X&lt;/span&gt;
    ${item}
  &lt;/li&gt;
&lt;/ul&gt;  
&lt;/template&gt;  
</code></pre>

<p><em>view.js</em></p>

<pre><code class="language-[language-javascript]">export class View {  
  items = ['foo', 'bar'];
  remove(item) {
    this.items.splice(this.items.indexOf(item));
  }
}
</code></pre>

<p><em>styles.css</em></p>

<pre><code class="language-[language-css]">@-webkit-keyframes swipeRight {
  0%   { -webkit-transform: translate3d(-120%, 0, 0); }
  100%   { transform: translate3d(0, 0, 0); }
}

@keyframes swipeRight {
  0%   { transform: translate3d(-120%, 0, 0); }
  100%   { transform: translate3d(0, 0, 0); }
}

@-webkit-keyframes swipeLeft {
  0%   { -webkit-transform: translate3d(0, 0, 0); }
  100%   { transform: translate3d(-120%, 0, 0); }
}

@keyframes swipeLeft {
  0%   { transform: translate3d(0, 0, 0); }
  100%   { transform: translate3d(-120%, 0, 0); }
}
.animated-collection {
  overflow: hidden;
  padding: 1em;
  margin: -1em;

  -webkit-animation-delay:50ms;
  animation-delay:50ms;
}
.animated-collection&gt;li.au-enter {
  transform: translate3d(-120%, 0, 0);
}

.animated-collection&gt;li.au-enter-active {
  -webkit-animation: swipeRight .4s;
  animation: swipeRight .4s;
}

.animated-collection&gt;li.au-leave-active {
  -webkit-animation: swipeLeft .4s;
  animation: swipeLeft .4s;
}
</code></pre>

<p>Et c'est tout.</p>

<p><code>animated-collection</code> est une classe arbitraire qui me permet de définir le style d'animation pour ma liste. Évidemment, vous pouvez vous en passer si vous préférez utiliser la même animation partout.</p>

<p>Il faudra donc définir les animations activées par <code>.au-enter-active</code> correspondant à l'entrée d'un nouvel élément dans le DOM et <code>.au-leave-active</code> correspondant à sa sortie. <br>
Il faudra aussi penser à définir l'état de départ de l'élément grâce à <code>.au-enter</code> pour que l'animation se passe correctement. Par exemple ici, je fais une translation horizontale, si je n'indique pas que l'élément doit d'abord être caché, alors il apparaitra immédiatement à sa place donc sans animation.</p>

<p>Il reste encore un petit truc, c'est la classe <code>au-stagger</code> appliqué au conteneur. Grâce à cette classe, Aurelia comprend qu'il doit appliquer les animation sur les éléments les uns après les autres plutôt que sur chacun en même temps. Donc selon l'effet que vous voulez afficher, vous pourrez donc soit afficher tous les items en même temps, ou les afficher l'un après l'autre comme sur la vidéo d'exemple.</p>]]></content:encoded></item></channel></rss>