Angular/Ionic: simuler un serveur avec des fichiers JSON

Je travaille actuellement sur un projet mobile Ionic et j’ai du mettre en place un mode démonstration avec des données fake. Au lieu de faire un appel au serveur, j’utilise des fichiers JSON qui contiennent les données à retourner.

Cela peut également être pratique si vous devez travailler sans être dépendant de la partie serveur.
Afin d’éviter de toucher le code métier (mes services angular) et faire des tests dans mes controllers/services pour savoir si je suis en mode démo ou pas, j’utilise un interceptor angular (la documentation angular sur $http et les interceptors) qui va faire la requête http ou renvoyer les bonnes de démonstration.

L’astuce ici consiste à contourner le cache de $http. Il faut modifier dans la fonction request de l’interceptor l’objet config pour définir une fonction get dans le cache. (Seulement les verbes HTTP GET et JSONP peuvent utiliser le cache $http. Je force un appel en GET mais garde en mémoire la méthode d’origine). Ainsi, l’appel serveur ne sera pas fait et la fonction définie en tant que cache sera appelée. Vous pouvez ainsi retourner vos données, dans notre cas à partir de fichiers json.

Voici le code de mon interceptor:

(function (module) {
  'use strict';
  module.factory('demoInjector', ['baseUrl', '$injector', function(baseUrl, $injector) {

    //check if request is done on backend and not templates for example 
    const isBackendRequest = function (config) {
      return config.url.indexOf(baseUrl) > -1;
    };

    var sessionInjector = {
      request: function(config) {
        if (!isBackendRequest(config)) {
          return config;
        }
        const demoService = $injector.get('DemoService');
        if (demoService.isDemoMode) {
          //store http method and change request method to GET because only GET and JSONP are cached
          config.originalMethod = config.method;
          config.method = 'GET';
          config.cache = {
            get: function() {
              return demoService.mock(config);
            }
          };
        }
        return config;
      }
    };
    return sessionInjector;
  }]);

  module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.unshift('demoInjector');
  }]);
})(angular.module('diagral'));

Dans le demoService, service qui contient la logique du mode démonstration, on se base sur l’url (via une RegExp) et la méthode HTTP (sauvegardée dans la propriété originalMethod via l’interceptor) de l’objet config pour déterminer le contenu approprié.

Voyons maintenant comment charger un fichier JSON inclus dans votre projet Ionic (en version 1). Pour ma part, les fichiers sont stockés dans /www/demo.
Sous Android, le dossier www est placé dans le dossier android_asset. Il faut donc adapter l’url du fichier cible en prenant compte de la platform cible via ionic.Platform. Il suffit ensuite de faire une requête via $http avec le chemin du fichier.

Les exemples ci-dessous sont en ES2015 avec des classes (d’où les this …)

this.url = '';
if (ionic.Platform.isWebView() && ionic.Platform.isAndroid()) {
    this.url = '/android_asset/www/';
}

this.$http.get(this.url + 'demo/myFile.json');

Afin de simuler un temps d’attente réaliste, vous pouvez utiliser $timeout.

const delay = 1000; //délai d'1 seconde
const responseDeferred = this.$q.defer();
this.$timeout(() => {
  this.$http.get(this.url + 'demo/myFile.json')
    .then(resp => responseDeferred.resolve(resp));
}, delay);
return responseDeferred.promise;

Il est également possible de renvoyer un objet JavaScript directement (si vous avez besoin de gérer un état par exemple). Il faut, dans ce cas, créer une réponse comme ceci:

const response = {
  config: config,
  status: 200,
  headers: () => {},
  data: () => angular.toJson({
          name: 'Julien',
          age: 35,
          lastUpdate: Date.now()
        })  
};

Voici le code final de la fonction mock qui utilise un tableau de configuration contenant les mocks avec la réponse attendue et les différentes options (délai, json/fonction, …).

mock(config) {
  const response = {
	config: config,
	status: 200
  };

  //Configuration du tableau des mocks
  const staticResponses = [
  {
    pattern: /login$/,
    jsonFile: 'login.json',
    method: 'POST'
  },
  {
    pattern: /article$/,
    data: () => this.getFakeArticles(),
    delay: 500
  },
  //...
  ];
  const staticResponse = this.findMock(staticResponses, config);
  if (staticResponse) {
    const r = this.$q.defer();
    const { delay = 200 } = staticResponse; //200ms by default if not defined
    this.$timeout(() => {
      if (staticResponse.jsonFile) {
        this.$http.get(this.url + 'demo/' + staticResponse.jsonFile)
          .then(resp => r.resolve(resp));
        return;
      }
      if (this._.isFunction(staticResponse.data)) {
        response.data = staticResponse.data(config.data, config.url);
        r.resolve(response);
        return;
      }
    }, delay);
    return r.promise;
  }

  response.status = 500;
  response.data = { message: 'Indisponible en mode démo'};
  return this.$q.reject(response);
}

findMock(staticResponses, { url: requestUrl, originalMethod: method}) {
  return this._.find(staticResponses, r => r.pattern.test(requestUrl) && method === (r.method || 'GET'));
}

.NET : Génération de jeu de données avec Nbuilder et Faker.Net

Nous avons parfois besoin de jeu de données factices pour nos tests unitaires, peupler une base de données ou pour simuler un service externe par exemple. Il peut être complexe et assez lourd de créer des jeu de données. Je vais vous présenter NBuilder et Faker.Net qui sont 2 librairies pour vous aider à générer vos jeux de données. Ces 2 librairies sont disponibles sous forme de package NuGet.

NBuilder

NBuilder est une librairie qui permet de créer facilement et rapidement des objets, basé sur le design pattern builder. Il va renseigner automatiquement pour vous les propriétés publiques de vos objets. Vous pouvez bien entendu préciser comment générer certaines propriétés.
Par défaut, NBuilder crée des valeur séquentielle dans le cas des liste (Name1, Name2, …)
NBuilder fournit une classe GetRandom qui permet de générer des données aléatoire pour un certains type de données (url, nom , pays, …).

//Création d'un objet
var product = Builder<Product>
    .CreateNew()
        .With(x => x.Name = "Nexus 5")	
	.With(x => x.Price = 199)
    .Build();

//Création d'une liste
var products = Builder<Product>
  .CreateListOfSize(10)
  .All()
  .With(x => x.Name = GetRandom.String(10))	
  .With(x => x.Price = GetRandom.Int(100, 400))
  .Build();

NBuilder fournit également Pick qui permet de sélectionner un objet dans une autre liste

//Sélection d'1 seul élément
Pick<Category>.RandomItemFrom(categories))
//Sélection d'un sous ensemble d'élément
Pick<Category>.UniqueRandomList(With.Between(5).And(10).Elements).From(categories);

Faker.NET

Faker.Net est un bon complément à NBuilder car il permet de générer des valeurs plus réaliste pour les données. Faker.Net contient plus de type de données que NBuilder.

Il contient des générateurs pour:

  • Addresse: (pays, code postal, ville, …)
  • Téléphone
  • Internet (email, password, IP, …)
  • Nom
  • Date
  • Entreprise
  • Finance (Prix, Compte, …)
//Utilisation de Faker.Net avec NBuilder
var customers = Builder<Customer>.CreateListOfSize(100)
        .All()
            .With(c => c.FirstName = Faker.Name.First())
            .With(c => c.LastName = Faker.Name.Last())
            .With(c => c.EmailAddress = Faker.Internet.Email())
            .With(c => c.TelephoneNumber = Faker.Phone.Number())
        .Build();

Un petit exemple d’utilisation avec .Net Fiddle

Liens

Components en angular 1.5

Un des grands changement d’Angular 2 est l’abandon des controller pour l’utilisation de components. Afin de facilité la migration, la version 1.5 d’Angular a introduit une méthode component() permettant de créer des composants. Cette version introduit également les concepts de one-way data binding et de lifecycle hooks.

Création d’un component

La création d’un composant est très simple. il suffit de renvoyer un objet comme dans l’exemple ci dessous.

var ProductComponent = {
  bindings: {
    name: '=',
    price: '='
  },
  template: `Name: {{$ctrl.name}}
             Price: {{$ctrl.price}}`
};

angular
  .module('app', [])
  .component('productComponent', ProductComponent);
<product-component name="Nexus 5" qty="299"></product-component>

On peut voir dans l’exemple ci dessus:

  • la déclaration des bindings via la propriété … bindings. Nous les déclarons de la même façon que pour les directives. Les bindings sont automatiquement bindés sur le controller (bindToController dans angular 1.4)
  • la déclaration de notre template (j’utilise ici un template string ES6). Notez qu’il est possible d’utiliser un template via un fichier html via templateUrl (de la même façon que les directives).
  • Les components utilisent des controller en ‘controller as‘ dont le nom par défaut est $ctrl. Vous pouvez bien entendu le modifier via la propriété controllerAs

Nous voyons que dans le cas de composants d’affichage, les composants sont beaucoup plus simple et concis.

One way data bindings

Une des nouveautés très intéressante de Angular 1.5 est l’apparition du one way binding via la notation ‘<‘. Les changements effectués sur une propriété bindée en one-way ne seront pas répercutés sur l’objet sur lequel il est bindé (sens component -> parent). Les mises à jour sur l’objet source sont répercutées sur le component (parent -> component). On gagne ainsi en performance et en maintenance.

Lifecycle hooks

La version 1.5 introduit également une notion de cycle de vie des components sur lequel nous allons pouvoir nous plugger.
Nous avons les événements suivant sur lesquels nous allons pouvoir nous abonner:

  • onInit : à l’initialisation du component. Idéal pour initialiser notre composants, definir les valeurs par defauts, …
  • onChanges: à chaque modification de valeur d’un binding one-way.
  • onDestroy: lors de la destruction du composant. Idéal pour libérer les ressources …

Tests

Une des choses appréciables avec angular est la testabilité. Les components sont très simple à tester. Les tests d’un controller d’un component se feront via l’utilisation de $componentController inclut dans ngMock. L’avantage est qu’on n’a pas besoin de créer un élément.

Migration d’une directive vers un component

J’ai déjà parlé d’une directive gérant un select avec des valeur numérique. Voyons comment la migrer vers un composant.

Liens

Conclusion

L’arrivée des components dans la version 1.5 d’angular a modifié la façon de développer des applications angular 1.x. Je vous conseille vivement leur utilisation, qui plus est si vous souhaitez préparer la migration vers angular 2.

ASP.NET MVC : Display et Editor Templates

ASP.NET MVC est un outil puissant pour créer des sites web, basés sur des conventions (Conventions over configurations). Nous allons voir dans cet article les notions de display et editor templates qui permettent la gestion du rendu d’un objet/propriété en mode affichage et édition.

Utilisation

ASP.NET MVC fournit par défaut des méthodes dans le helper Html permettant la gestion de l’affichage (DisplayFor) et l’édition (EditorFor).

@Html.DisplayFor(model => model.Title)

@Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })

Par défaut, le helper générera un input en fonction du type des propriétés de votre objet. Ainsi, dans le cas d’une propriété de type string, un input de type text sera généré et dans le cas d’un bool, une case à cocher. Il est possible d’avoir un contrôle plus fin de la génération des input. Pour cela, le helper HTML met à disposition des méthodes pour chaque type de input :

  • Html.TextBox
  • Html.DropDownList

Cela est très pratique et permet de mieux contrôler le HTML généré mais vous êtes obligé de le faire dans chaque vue. Afin de palier à ce problème, il existe les DisplayTemplates et EditorTemplates. Ils permettent de définir pour chaque type comment il sera rendu en mode affichage (display) ou édition (editor). Pour cela, il faut créer des dossiers DisplayTemplates et EditorTemplates dans le dossier Views/Shared puis placer un fichier par type en le nommant type.cshtml.

Dossiers Display et Editor Template

Dans les exemples suivant, nous allons utiliser une classe Album (les annotations de validations sont volontairement omises) :

public class Album
{
    public int ID { get; set; }

    public string Title { get; set; }

    public DateTime ReleaseDate { get; set; }

    public bool isDoubleDisc { get; set; }
}

Par exemple, pour surcharger le template utilisé pour les types DateTime et bool, on créera respectivement un fichier DateTime.cshtml et Boolean.cshtml.

Nous avons vu que les booléens sont affichés sous la forme de checkbox. Voyons comment rendre votre application un peu plus jolie avec des composants ayant un meilleur rendu.
J’ai parlé dans un article précédent des web components et de polymer.

Polymer fournit une bibliothèque de composant (elements dans le jargon Polymer) prêt à l’emploi, notamment les Paper Elements, composant graphiques Material Design.

Voyons comment utiliser des paper-checkbox (la page de démo) pour les booléens grâce aux display templates.

Afin d’utiliser polymer et les elements, il faut les ajouter à la solution. Voici la documentation de polymer permettant de le faire.

Ajoutons un fichier Boolean.cshtml dans le dossier Views/Shared/DisplayTemplates :

@model System.Boolean
@if(@Model) {
    <paper-checkbox disabled checked></paper-checkbox>
} else {
    <paper-checkbox disabled></paper-checkbox>
}

L’affichage utilise maintenant un élement paper-checkbox pour les booléens.

Display Template sous la forme d'un Checkbox

Vous avez peut être remarqué que Polymer propose également un élément paper-toggle-button qui convient également pour les booléens.
ASP.NET MVC contient un attribut UIHint qui permet de spécifier un template à appliquer pour une propriété.

Ajoutons l’attribut UIHint avec la valeur Toggle sur la propriété IsDoubleDisc.

[UIHint("Toggle")]
public bool IsDoubleDisc { get; set; }

Définissons maintenant un fichier Toggle.cshtml dans le dossier DisplayTemplates avec le code suivant :

@model System.Boolean
@if(@Model) {
    <paper-toggle-button disabled checked></paper-toggle-button>
} else {
    <paper-toggle-button disabled></paper-toggle-button>
}

Automatiquement, le champs IsDoubleDisc sera affiché avec un toggle !

Display Template sous la forme d'un Toggle

Il est également possible de créer des templates pour vos propres modèles.

Voici un exemple avec une classe Address :

@model DemoMVC5.Models.Address
 <dl class="dl-horizontal">     
     <dt>
         @Html.DisplayNameFor(model => model.Street)
      </dt>
      <dd>
         @Html.DisplayFor(model => model.Street)
      </dd>
      <dt>
         @Html.DisplayNameFor(model => model.ZipCode)
      </dt>
      <dd>
         @Html.DisplayFor(model => model.ZipCode)
      </dd>
      <dt>
         @Html.DisplayNameFor(model => model.City)
      </dt>
      <dd>
         @Html.DisplayFor(model => model.City)
      </dd>
      <dt>
         @Html.DisplayNameFor(model => model.Country)
      </dt>
      <dd>
         @Html.DisplayFor(model => model.Country)
      </dd>
  </dl>

Vous pourrez alors utiliser @Html.DisplayForModel et éviter de dupliquer votre code dans plusieurs vues.

@model DemoMVC5.Models.Address
<div>
    <h4>Your Address</h4>
    Html.DisplayForModel()
</div>

Si un de vos objets a une propriété Address, l’affichage se fera via :

@model DemoMVC5.Models.User
<div>
    <h4>Your Information</h4>
    Html.DisplayFor(model => model.Name)

    Html.DisplayFor(model => model.Address)
</div>

Conclusion

Display et Editor templates sont des fonctionnalités méconnues de ASP.NET MVC. Elle permettent de facilement modifier l’affichage d’une classe dans votre site de manière simple et sans duplication de code.

Liens

JavaScript : les nouvelles méthodes sur l’objet Array apportées par ES6/ES2015

ES5 a apporté de nouvelles méthodes à l’objet Array que j’avais abordés dans un article précédent. La nouvelle version ES2015 (anciennement ES6) apportent également quelques nouveautés que nous allons voir.

Array.prototype.find

La méthode find() permet de retourner le premier élément d’un tableau respectant une condition via une fonction passée en paramètre. Si aucun element ne satisfait le critère, undefined est retourné.

const numbers = [1, 2, 3, 4, 5, 6];
let estPair = function (n) {
    return n % 2 === 0;
};
let firstEven = numbers.find(estPair); //2

La documentation sur MDN

Array.prototype.findIndex

findIndex() est identique a find à la différence qu’elle retourne l’index du premier élément à la place de l’élément lui même. Si aucun element ne satisfait le critère, -1 est retourné.

const numbers = [1, 2, 3, 4, 5, 6];
let estPair = function (n) {
    return n % 2 === 0;
};
let firstEvenIndex = numbers.findIndex(estPair); //1

La documentation sur MDN

Array.prototype.entries

La méthode entries() retourne un nouvel itérateur (Array Iterator) qui contient les clés/valeurs pour chaque index du tableau.

const arr = ['a', 'b', 'c'];
for (let item of arr) {
    console.log(item); // => 'a' 'b' 'c'
}
for (let pair of arr.entries()) {
    console.log(pair); // => [0, 'a'] [1, 'b'] [2, 'c']
}

La documentation sur MDN

keys

La méthode keys() retourne un nouvel itérateur (Array Iterator) contenant les clés pour chaque index du tableau.

const arr = [1, 2, 3];
const arrIterator = arr.keys();
let nextVal
while (nextVal = arrIterator.next().value) {
 console.log(nextVal);
}
// => 1
// => 2
// => 3

La documentation sur MDN

Array.of

La méthode (statique) Array.of() créé une nouvelle instance d’un tableau contenant les valeurs passés en paramètres, quelque soit le type ou le nombre de paramètres.

Array.of(1); // => [1]
Array.of(1,2,3); // => [1,2,3]

La documentation sur MDN

Array.from

La méthode (statique) Array.from() créé une nouvelle instance d’un tableau à partir d’un objet assimilés à un tableau array-like (comme arguments ou des noeuds du DOM) ou d’un objet iterable.

On peut utiliser Array.from() depuis arguments, des éléments du DOM, Map, Set, String…

// Array-like object (arguments) to Array
function f() {
  return Array.from(arguments);
}

f(1, 2, 3); 
// [1, 2, 3]

// Any iterable object...
// Set
var s = new Set(["foo", window]);
Array.from(s);   
// ["foo", window]

// Map
var m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m);                          
// [[1, 2], [2, 4], [4, 8]]  

// String
Array.from("foo");           
// ["f", "o", "o"]

// Using an arrow function as the map function to
// manipulate the elements
Array.from([1, 2, 3], x => x + x);      
// [2, 4, 6]

// Generate a sequence of numbers
Array.from({length: 5}, (v, k) => k);    
// [0, 1, 2, 3, 4]

La documentation sur MDN

ES7 / ES2016

La version 7 ou 2016 devrait également apportés d’autres nouveautés (encore en cours de validation …).

Liens

La page MDN sur l’objet Array
Une présentation sur l’utilisation des tableaux et objets : Working with Arrays and Objects in modern JavaScript (les slides) et la vidéo :

Il existe des polyfills pour ces méthodes. Vous pouvez également utiliser underscore ou lodash qui propose ses méthodes depuis un moment et bien d’autres .. (la documentation concernant les Arrays de underscore).

Angular : Utiliser les classes ES6

J’ai présenté dans un article précédent comment utiliser Babel afin de coder en ES6/ES2015 et transpiler vers ES5.

Angular 2 a été annoncé il y a 1 an à la ng-conf Europe et est aujourd’hui en version bêta. Cette nouvelle version est une réécriture complète qui va changer beaucoup de choses (plus de scope, controlleur, angular.modules, …). La version 1.x d’Angular a 5 ans et beaucoup de choses ont évolués depuis. 3 changements majeurs expliquent ce choix :

Pour info, il sera possible de coder pour la version 2 en ES5, ES6 ou TypeScript (et même dart).

Si vous décider de coder en ES6 ou Typescript, vos services ou vos composants seront des classes ES6. Celles-ci simplifient grandement l’écriture et leur utilisation dans Angular 1.x facilitera la migration vers la version 2.

Voyons maintenant comment utiliser les classes ES6 pour Angular. Les exemples suivants proviennent de mon application de Volley disponible sur github.

Commun

Vos services, controller, directives vont être définis comme des classes et l’injection va se passer par le constructeur. Il va falloir « enregistrer » vos dépendances sur votre objet (sur this) afin de pouvoir les utiliser dans vos méthodes. Attention, on oublie facilement le this !!

Controller

L’utilisation des classes ES6 n’empêchent pas d’utiliser $scope mais la migration en classe est un bon moyen de migrer vers la syntaxe « controller as ». Cette syntaxe n’utilise pas le scope qui n’existe pas en Angular 2.

Avant
Après

Services

Vous savez qu’il y a différent types de service en Angular : services, factory, provider.

Les services de type service sont conçus pour définir un service sous forme de classe (ou comme type instantiable)
Si vous avez des factory, le plus simple est de les convertir en service.

Dans le cas d’un provider, il faut que la classe contienne une propriété nommée $get, qui doit être une fonction qui retourne une factory.

Avant
Après

Directives

Les directives sont les éléments les plus complexes et les plus difficiles à appréhender en Angular 1.x. Attention contrairement aux autres types, il vous faudra instancier vous même vos services.

Avant
Après

Cet exemple n’a aucune dépendance injectée. Si votre directive contient des dépendances il faut les passer en paramètre lors de instanciation de votre directive.

https://gist.github.com/royto/dd448131ab13d9d31d9c

Conclusion

Les classes ES6/2015 permettent de simplifier le code et facilitera une potentielle migration vers Angular 2.
Cet article n’utilise que les classes ES6 mais il est possible d’utiliser d’autres nouveautés comme les modules ou les décorateurs par exemple. Vous trouverez plus d’informations dans les liens ci dessous.

Liens

Angular 2 :

ES6 : quelques nouveautés de la prochaine version de JavaScript – Part III

Cet article est la suite d’une série d’articles sur ES6 :

Avant de commencer, une petite information en terme de nommage, ES6 a été renommé en ES2015.

Promise

Définissons tout d’abord ce qu’est une promesse :
Une promesse représente l’éventuel résultat d’une opération asynchrone

La notion de promise (promesse en français) n’est pas nouvelle et existe dans différentes librairies sous différentes formes comme :

Les Promise seront inclus de manière native dans ES6.

Les promise permettent de gérer plus facilement les méthodes asynchrones et d’éviter un code difficile à lire et maintenable si on utilise des callback. En effet on arrive facilement à avoir du code comme cela :

step1(function (value1) {
    step2(function (value2) {
        step3(function (value3) {
            step4(function (value4) {
                step5(function (value5) {
                    step6(function (value6) {
                        // Do something with value6
                    });
                });
            });
        });
    });
});

C’est ce qu’on appelle pyramid of doom ou callback hell.

Un nouvel objet Promise est introduit dans ES6. Le constructeur prend en paramètre une fonction avec 2 fonctions en paramètre : resolved et reject. La fonction resolve est à appeler si il n’y a pas eu d’erreur avec les valeurs de retour. Elle permet d’indiquer que la promise est résolue permettant la suite des actions. La fonction reject est à appeler si une erreur est survenue.

Création d’une Promise

    var promise = new Promise(
        function (resolve, reject) { // (A)
            ...
            if (...) {
                resolve(value); // success
            } else {
                reject(reason); // failure
            }
        });

Une fois notre promise définie, nous allons pouvoir définir un handler sur cette promise afin de réaliser des actions sur la future valeur. L’enchaînement des actions se fait via la méthode then. Cette fonction prend en paramètre 2 fonctions: la 1ere pour le succès (resolve) et la deuxième lors d’une erreur (reject). Les erreurs peuvent être également gérés via la méthode catch.

Utilisation d’une promise

 httpGet('http://myServer.com/file.txt')
    .then(
        function (value) { //Success
            console.log('Contents: ' + value);
        },
        function (reason) { //Error
            console.error('An error occurred', reason);
        });

L’avantage des promises est qu’on peut facilement les chaîner, ou les composer.

Fetch, la nouvelle API pour faire des appels Ajax utilise les promises. (un article pour aller plus loin)

Il existe des polyfills afin de pouvoir les utiliser dès maintenant :

Quelques liens sur le sujet :

Destructuring

Le destructuring permet d’initialiser des variables en les extrayant à partir d’objet existant.

//Array : on extrait 3 variables m, d et y à partir d'un tableau
var [m, d, y] = [12, 21, 1981]; 

//object : on extrait les propriétés d'un objet
//Ici on définit une fonction qui extrait la liste des classe CSS d'un élément
var listOfCLass = function ({ classList } ) {
    Array.from(classList).forEach( (item) => console.log(item))
};
listOfCLass(document.body);

Une autre utilisation avec un module quand on ne veut importer qu’une partie d’un module. (cf chapitre module dans la partie II).

Quelques liens :

ES7

La future version d’EcmaScript est déjà en préparation et je vais parler de 2 des nouveautés qui sont à mes yeux les plus intéressantes.

Object.observe

Object.observe() est une nouvelle API qui permet de s’abonner aux changements effectués sur un objet. A chaque modification sur l’objet, un événement sera lancé contenant la liste des changements (ajout d’une propriété, modification d’une valeur, suppression d’une propriété) sur l’objet.

Voici un exemple tirée de la documentation de Object.observe sur MDN :

var obj = {
  foo: 0,
  bar: 1
};

Object.observe(obj, function(changes) {
  console.log(changes);
});

obj.baz = 2;
// [{name: 'baz', object: &amp;amp;amp;lt;obj&amp;amp;amp;gt;, type: 'add'}]

obj.foo = 'hello';
// [{name: 'foo', object: &amp;amp;amp;lt;obj&amp;amp;amp;gt;, type: 'update', oldValue: 0}]

delete obj.baz;
// [{name: 'baz', object: &amp;amp;amp;lt;obj&amp;amp;amp;gt;, type: 'delete', oldValue: 2}]

Cela permet notamment de facilement mettre en place du data-binding (Angular 2 l’utilise afin d’améliorer les performances et éviter le « dirty checking »).

Quelques liens sur le sujet :

Async/Await

ES6 simplifie, via l’introduction des promesses, grandement la gestion de code asynchrone. Async va encore plus loin afin de faciliter la lecture et la compréhension.
ES7 introduit 2 nouveaux mots clés : async et await.

async permet de définir une fonction comme étant asynchrone alors que await permet d’indiquer que l’on souhaite bloquer l’exécution du code jusqu’au retour de la fonction.

Voici un exemple d’utilisation :

async function getUserName() {
    return 'royto';
}

async function displayLogoutMessage() {
    var user = await getUserName();
    alert(`GoodBye ${user} !`);
}

Pour ceux qui viennent du monde .NET cette syntaxe ne devrait pas les surprendre 🙂

Quelques liens :

Liens sur ES6 / ES2015

Angular : Directive avec controller as, bindToController et tests

Une des bonnes pratiques promue par les experts angular est l’utilisation des controller en utilisant la syntaxe ‘controller as’ (des liens sur le sujet en bas de l’article).
En effet, cette pratique permet d’éviter certains écueils lié au prototypage JavaScript et permet de ne plus se coupler au scope.
La notion de scope n’existera plus dans angular 2 (de même que les controller d’ailleurs …) et il est donc conseillé de limiter son utilisation afin de faciliter une migration.

Les équipes d’angular travaillent sur les versions 1.x afin de faciliter la migration et contiennent donc des améliorations pour cela.

La version 1.3 a introduit la propriété bindToController qui permet d’automatiquement de rendre disponible les propriétés déclarés sur le scope dans le controller d’une directive.

La version 1.4 permet de définir directement sur bindToController les propriétés comme on le faisait sur le scope.

Directive avec controller as

Voici un exemple d’une directive tirée de mon application de volley ou l’on déclare une directive avec un controller en utilisant la syntaxe controller as et bindToDirective.

function matchScore() {
  return {
    templateUrl : 'views/directives/match-score.html',
    restrict : 'E',
    scope : true,
    bindToController : {
        match : '='
    },
    controllerAs : 'matchScore',
    controller : function () {
      return {
        scoreSets : function (set, team) {
          return this.match.score[set]
	    .filter(val => val === team)
            .length;
        }
      }
    }
  }
}

angular.module('volleyApp').directive('matchScore', matchScore());

Dans le vue de notre directive, il faut alors utiliser matchScore comme préfixe pour nos bindings.

<div class="list-group-item-text">
    <table class="table table-bordered score">
    <tbody>
    <tr>
        <th>Team</th>
        <th>Set 1</th>
        <th>Set 2</th>
        <th>Set 3</th>
        <th>Set 4</th>
        <th>Set 5</th>
    </tr>
    <tr id="scoreTeam1">
        <th><span id="team1Name">{{matchScore.match.teams.team1}}</span></th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 1)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 1)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 1)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 1)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 1)}}</span></td>
    </tr>
    <tr id="scoreTeam2">
        <th><span id="team1Name"></span>{{matchScore.match.teams.team2}}</th>
        <td class="set1"><span>{{matchScore.scoreSets(0, 2)}}</span></td>
        <td class="set2"><span>{{matchScore.scoreSets(1, 2)}}</span></td>
        <td class="set3"><span>{{matchScore.scoreSets(2, 2)}}</span></td>
        <td class="set4"><span>{{matchScore.scoreSets(3, 2)}}</span></td>
        <td class="set5"><span>{{matchScore.scoreSets(4, 2)}}</span></td>
    </tr>
    </tbody>
</table>

Tests

Si on utilise pas controller as, les méthodes du controller sont accessible via le scope.

Si on utilise controllerAs, nous n’avons plus de scope pour accéder aux méthodes de notre controller. Comment alors tester nos méthodes de controller ?

Angular fournit des méthodes additionnelles à element (élément jQuery/jqLite) et fournit notamment une méthode element.controller qui permet de récupérer un controller. Il faut l’appeler avec le nom de la directive (et non par celui définit via controller as).

//on récupère le controller via element 
var ctrl = element.controller('matchScore');

//On teste nos méthodes
expect(ctrl.scoreSets(0, 1)).toBe(25);

Liens

JavaScript : Utiliser ES6 maintenant avec Babel

Je vous ai parlé dans des articles précédent de la nouvelle version de JavaScript, ES6/ES2015, Harmony.
J’avais évoqué des outils afin de pouvoir utiliser ces nouveautés dés maintenant, malgré le support partiel dans les navigateurs ou dans de vieux navigateurs.

Babel est transpileur qui va transformer du code ES6 en code compatible ES5 (version compatible dans la plupart des navigateur (IE ….). Babel est la fusion de 2 projets : 6to5 et esnext.

Il supporte l’ensemble des nouveautés de ES6 et même certaines fonctionnalités de ES7. Il a l’avantage de produire un code compréhensible et ne nécessite pas l’inclusion d’un script additionnel dans votre page (comme traceur, un autre transpileur).

Babel est basé sur node et il existe des plugins pour la plupart des task runner JavaScript comme Grunt, Gulp, …

Voyons comment l’utiliser avec grunt.

Installation et utilisation

Tout d’abord, installons le package grunt pour babel. L’installation se fait via la commande :

npm install grunt-babel --save-dev

Nous définissons une tache nommée babel dans notre Gruntfile.js, qui va prendre nos fichiers écrit en ES6 et les transpiler en ES5.

J’ai configuré une tache avec 2 configurations, une pour le dev et une pour générer un package, dist.

 //transpilation to ES5
  babel: {
    options: {
      sourceMap: true,
      blacklist: ["strict"]
    },
    dev: {
      files: [{
        expand : true,
        cwd: '<%= yeoman.app %>/scripts/',
        src: ['**/*.js'],
        dest: '<%= yeoman.dist %>/scripts/',
        ext: '.js'
      }]
    },
    dist: {
      files: [{
        expand : true,
        cwd: '<%= yeoman.tmp %>/concat/scripts',
        src: '*.js',
        dest: '<%= yeoman.tmp %>/concat/scripts',
        ext: '.js'
      }]
    }
  }

Il est possible d’activer des options. Dans mon cas, j’ai activé les sourcesmaps et supprimé l’option strict qui rajoute les « use strict » en début de fichier (ils sont déjà présent dans mes fichiers).

Il nous faut maintenant l’inclure dans nos taches grunt. Je l’inclus dans ma tache serve qui permet d’avoir un serveur web.

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bowerInstall',
      'concurrent:server',
      'autoprefixer',
      'copy:dist',
      'babel:dev',
      'connect:livereload',
      'watch'
    ]);
  });

Bien entendu, nous pouvons configurer grunt pour transpiler notre code à la volée et recharger la page via watch et livereload pour un process de développement plus fluide :

 watch: {
  js: {
    files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
    tasks: ['newer:eslint:all', 'newer:babel:dev'],
    options: {
      livereload: true
    }
  },
  //others file types ...
}

Exemple de Code généré

Voici du code ES6 que nous allons transpiler avec babel (let, expression lamda, string templates, classes)

//Exemple avec une expression lambda
var double = x => x * 2;

//string template
var deux = 2;
let quatre = double(deux);
console.log(`${quatre} est le double de 
            ${deux}`);

//Exemple avec une classe
class Person {
  constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }
  fullname() {
    return this.firstname + ' ' + this.lastname; 
  }
}

let julien = new Person('Julien', 'Roy');
console.log(julien.fullname());

Et voici le code généré par Babel

//Exemple avec une expression lambda
'use strict';

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var double = function double(x) {
  return x * 2;
};

//string template
var deux = 2;
var quatre = double(deux);
console.log('' + quatre + ' est le double de \n            ' + deux);

//Exemple avec une classe

var Person = (function () {
  function Person(firstname, lastname) {
    _classCallCheck(this, Person);

    this.firstname = firstname;
    this.lastname = lastname;
  }

  _createClass(Person, [{
    key: 'fullname',
    value: function fullname() {
      return this.firstname + ' ' + this.lastname;
    }
  }]);

  return Person;
})();

var julien = new Person('Julien', 'Roy');
console.log(julien.fullname());

Liens

  • REPL: une page qui permet de tester la conversion de votre code

.Net : Utilisation de Aggregate de LINQ

La version 3.5 de .NET a introduit LINQ qui permet de faciliter le requetage de source de données hétérogène (base de données, fichier XML, objects, …).

LINQ s’inspire beaucoup de la programmation fonctionnelle.

LINQ contient Lesplusieurs fonctions d’agrégations comme : Sum, Average, Count, Max et Min. Aggregate est la plus flexible et générique méthode d’agrégation.

la fonction Aggregrate permet d’itérer sur la collection et d’appliquer une fonction sur chaque élément afin de calculer un résultat. Dans les langages (JavaScript, F#, …) ou librairies fonctionnelles (lodash/underscore), la fonction s’appelle généralement reduce.

Voici un petit exemple d’utilisation de Aggregate qui permet de calculer la somme totale des prix * quantité d’une liste de produits :

Dans l’exemple ci dessus, current corespond a la valeur calculée suite à l’exécution des éléments précédents de la collection.
Ainsi dans notre exemple, pour le premier élément, current vaut 0. Puis 3 * 399,99 soit 1199,97 pour le deuxième et 3 * 399,99 + 100 * 399,99 soit 5199,87 pour le troisième élément.

Sachez qu’Aggregate a également une signature qui permet de passer une valeur par défaut pour current.

Liens