# Le Guide Angular par Marmicode

Après plusieurs années de développement, de consulting Angular, de formation et d'accompagnement d'entreprises dans leurs développements, nous avons décidé de produire cet ouvrage gratuit afin de partager notre expérience avec la communauté Angular.

## Nos objectifs

* Produire **rapidement** des applications **performantes**, **robustes** et **maintenables**.
* Privilégier le **pragmatisme** et mettre l'accent sur les **bonnes pratiques**.
* Partager le fruit de nos heures de **veille**, de **recherche** et de nos **retours d'expérience**.
* Répondre aux besoins des **développeurs "Enterprise"**.
* **Regrouper** l'information.
* Produire un **guide** **gratuit**, **à jour** et **communautaire.**
  * N'hésitez donc pas à nous envoyer vos Pull Requests sur : <https://github.com/wishtack/gitbook-guide-angular>

## Prochaines Sessions de Formation Angular

### [Formation Angular](https://guide-angular.wishtack.io/nos-formations/angular)

### [Formation Angular Architecture](https://guide-angular.wishtack.io/nos-formations/angular-architecture)

### [Formation Angular Testing](https://guide-angular.wishtack.io/nos-formations/angular-testing)

## Besoin d'aide ?

### Réservez une [Live Session](https://guide-angular.wishtack.io/nos-formations/live-session) !

## Copyright

{% hint style="warning" %}
Ce guide est l'oeuvre et la propriété de Marmicode.

Il ne peut être utilisé partiellement ou intégralement comme support de prestations rémunérées sauf par l'équipe Marmicode.
{% endhint %}

En cas de doute, merci de contacter l'équipe Wishtack : <contact@wishtack.io>

©Wishtack


# Pourquoi Angular ?

## Angular est un framework

Contrairement à d'autres alternatives intéressantes telles que [React](https://reactjs.org/), Angular n'est pas une "library" mais bien un framework avec une approche "batteries included".

Angular fournit donc nativement tout le nécessaire pour produire une application entière avec une configuration standard :

* **Configuration** de build et d'optimisation **complète**.
* Module d'**animations**.
* Module de "**routing**".
* Module de **formulaires**.
* **Debug**.
* **Tests unitaires** et **e2e**.
* ...

Il n'est donc pas nécessaire d'hésiter et de débattre le choix des modules de routing, de formulaires etc...

Avec Angular, la majorité des applications ont la même structure de projet et la même "stack" d'outils.\
**Les applications Angular sont donc homogènes** et vous tomberez donc plus rarement sur des "cas particuliers".

Dans la plupart des cas, vous éviterez les problèmes de compatibilité de dépendances en laissant l'équipe Angular s'en charger pour vous.

En contrepartie, voici un exemple de mise en place d'un "preprocessor" sass sur une app React:

![source: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc](/files/-Lcb-Ly8qVf6APr6XpZb)

Heureusement, les choses ont évolué depuis grâce à [create-react-app](https://facebook.github.io/create-react-app/) & react-scripts.

### Point de vue de Vue.js concernant Angular

![Opinionated Frameworks vs. Flexibility](/files/-LXtl6HblX-B3odZv56R)

Source: <https://vuejs.org/v2/guide/comparison.html#Flexibility>

## TypeScript

Angular profite de la **rigueur et flexibilité** du langage TypeScript.

## Single-Page App & Progressive Web App

Angular fournit nativement le nécessaire pour produire des **Progressive Web Apps**.

On peut donc rapidement produire des applications webs donnant l'illusion d'une application native tout en restant résilient aux problèmes de connexion.

L'idée est de voir le web comme des applications interreliées plutôt que des sites web contenant des pages.

{% embed url="<https://developers.google.com/web/progressive-web-apps/>" %}

## Abstraction

Angular fournit des couches d'abstraction à tous les niveaux. Cela évite par exemple de manipuler directement le DOM.

## Mobile

Par défaut, Angular repose sur la couche `PlatformBrowser` pour communiquer avec le DOM.

D'autres alternatives de cette couche existent pour utiliser Angular dans d'autres contextes :

* `PlatformNativeScript` : Utilisation de Native Script pour faire le bridge entre le code Angular et le code natif Android ou IOS. <https://docs.nativescript.org/angular/start/introduction>
* `PlatformServer` : Pour générer le rendu HTML côté serveur *(Server Rendering)*. <https://universal.angular.io/>
* Ionic qui permet de produire des applications natives contenant des "web views" Angular <https://ionicframework.com/docs/>
* Terminal <https://medium.com/angular-in-depth/angular-platforms-in-depth-part-3-rendering-angular-applications-in-terminal-117e4da9c0cc>

{% embed url="<https://medium.com/angular-in-depth/angular-platforms-in-depth-part-3-rendering-angular-applications-in-terminal-117e4da9c0cc>" %}
Angular Terminal
{% endembed %}

## Separation of concerns

Angular permet de mieux séparer les responsabilités avec une approche MVC *(Model / View / Controller)* et l'injection de dépendances.

## Ecosystème très riche et large communauté

Exploitation des 5 ans d'existence d'AngularJS et des retours de la communauté pour rectifier les erreurs de conception, faciliter le développement et gagner en performance et stabilité.

![Angular Platform](/files/-LfVgg9R3goq0xVtKaFX)

## Testabilité comme priorité

Angular fournit tous les outils nécessaires pour faciliter l'implémentation des tests unitaires et de tests e2e.

* Configuration clé en main des outils de test, du reporting, du coverage etc...
* Mocks des modules http et routing.
* Protractor : surcouche de Selenium adaptée pour faciliter les tests end-to-end Angular avec intégration native de la parallélisation et d'outils tels que [BrowserStack](https://www.browserstack.com/) et [SauceLabs](https://saucelabs.com/).

## Architecture et maintenabilité

AngularJS était très flexible. Sans bonnes pratiques, les applications perdent très rapidement en stabilité et en maintenabilité.

Angular impose **une approche mieux structurée** à base de composants et une façon plus clair d'échanger les données entre les composants.

Angular encourage une **implémentation générique (*****"framework agnostic"*****)** permettant de réutiliser plus facilement du code Angular dans d'autres contextes.

## Performance

Angular est un **framework** très **déclaratif**. Cela peut sembler fastidieux au départ mais cette approche permet à Angular de mieux comprendre le fonctionnement de l'application avant son exécution permettant alors d'optimiser la construction de l'application, par exemple en éliminant automatiquement le code inutile.

Au fil des mises à jour d'Angular, vous obtiendrez donc des applications de plus en plus performantes sans changer votre code ou à la rigueur en éliminant les usages dépréciés.

![How angular loses weight](/files/-LU4zlPiQbo11ZZ51m9X)

## Angular Release Schedule & Long-Term Support

L'équipe Angular s'engage à publier une **nouvelle version majeure tous les 6 mois** *(correspondant aux deux grandes conférences mondiales en Mai \[*[*NgConf*](https://www.ng-conf.org/)*], et en Novembre \[*[*Angular Connect*](http://angularconnect.com/)*] )*.

* **Angular 2** : Septembre 2016
* **Angular 4** : Mars 2017
* **Angular 5** : Novembre 2017
* **Angular 6** : 3 mai 2018 *(Angular CLI, Angular Material, Flex Layout etc... synchronisent leurs versions également)*
* **Angular 7** : 18 octobre 2018
* **Angular 8** : 28 mai 2019
* **Angular 9** : 2 février 2020
* **Angular 10** : 24 juin 2020
* **Angular 11** : **11-11**-2020
* **Angular 12** :  12 mai 2021
* **Angular 13** : 4 novemre 2021&#x20;

Chaque version est **maintenue pendant 18 mois** et reste compatible avec les fonctionnalités de la version précédente qui deviennent dépréciées.

<https://angular.io/guide/releases>

### Guide de mise à jour

<https://update.angular.io/>

{% embed url="<https://update.angular.io/>" %}

### A propos d'AngularJS

AngularJS correspond à la version 1.x Il ne faut pas confondre AngularJS et Angular. Bien que le numéro de version 2.x semblerait indiquer une continuité avec la version 1.x de Angular, il s'agit d'une refonte intégrale du Framework.

La dernière version de AngularJS, **AngularJS 1.7**, la dernière version de AngularJS est sortie en Mai 2018 et rentre en Long-Term Support (LTS) à partir du **1er Juillet 2018** jusqu'au **30 juin 2021**.


# ECMAScript 6+


# Un Peu d'Histoire

* **1995 :** Netscape crée le langage dynamique JavaScript pour faciliter le développement côté navigateur.
* **1995 :** Netscape rend possible l'implémentation d'applications côté serveur en JavaScript avec "Netscape Enterprise Server".
* **1997 :** Création du standard "cross-browser" et "cross-platform" ECMAScript.
* **1998 :** ECMAScript 2.
* **1999 :** ECMAScript 3.
* **2006** : JQuery
* **2009 :** ECMAScript 5 *(a.k.a. ECMAScript 3.1)*.
* **2009 :** Sortie de NodeJS.
* **Juin 2011 :** Finalisation du standard ECMAScript 5.1.
* **Juin 2015 :** Finalisation du standard ECMAScript 6.
* **Juin 2016 :** Finalisation du standard ECMAScript 7.
* **Juin 2017 :** Finalisation du standard ECMAScript 8.
* **Juin 2018 :** Finalisation du standard ECMAScript 9.


# Propriétés du Langage

## Typage

JavaScript est un langage **dynamiquement** et **faiblement** typé.

![](/files/-LAEEgb92HmErwla8pn_)

## JavaScript est un langage multi-paradigme

* Programmation fonctionnelle.
* Programmation orientée objet.
* Programmation reactive.

## JavaScript est cross-browser et cross-platform


# "Single-Threaded" donc Asynchrone

## Multi-thread vs mono-thread

Dans un univers data-driven synchrone et "multi-threaded"...

```javascript
function processRequest(request) {

    /*
     * Pre-processing.
     */
    var query = prepareQuery(...);

    var result = execQuery(query); // might take few seconds...

    /*
     * Post-processing.
     */
    var response = createResponse(result);

    return response;

}
```

...mais dans un univers "single-threaded" cela peut être catastrophique car l'application est bloquée tant qu'elle est en attente.

Tant qu'on ne sort pas de la fonction, aucune autre fonction ne peut être exécutée.

## Besoin de traitement asynchrone

```javascript
function processRequest(request, callback) {

    /*
     * Pre-processing.
     */
    var query = prepareQuery(...);

    execQuery(query, function (result) {

        /*
         * Post-processing.
         */
        var response = createResponse(result);

        callback(response);

    });

}
```

## Avantages de l'approche asynchrone

* Pas de limitation due au nombre de threads.
* Pas de locks ou sémaphores.
* Pas de locks gourmants.
* Pas de deadlock.
* Les données ne peuvent pas varier lors de l'exécution d'une fonction synchrone.

## Closure

Un "closure" est la combinaison d'une définition de fonction ainsi que l'environnement lexical dans lequel cette dernière est définie.\
Les "closures" déterminent la portée des variables.

Les variables sont accessibles en lecture / écriture dans le "closure" où elles sont déclarées mais également dans les fonctions définies à l'intérieur.

```javascript
function main() {

    var userName = 'Foo';

    function setUserName(value) {
        userName = value;
    }

    function getUserName() {
        return userName;
    }

    setUserName('John');

    console.log(getUserName()); // 'John';

}

main();
```


# Event Loop

## Comportement de l'Event Loop

Quel est l'ordre d'exécution ?

{% tabs %}
{% tab title="🧐" %}

```javascript
var value;

setTimeout(function () {
    value = 'VALUE';
}, 100 /* 100 ms. */);

console.log(value); // ???

setTimeout(function () {
    console.log(value); // ???
}, 200);
```

{% endtab %}

{% tab title="👍" %}

```javascript
var value;

setTimeout(function () {
    value = 'VALUE';
}, 100 /* 100 ms. */);

console.log(value); // 1 - undefined

setTimeout(function () {
    console.log(value); // 2 - VALUE
}, 200);
```

{% endtab %}
{% endtabs %}

Et dans ce cas ?

{% tabs %}
{% tab title="🧐" %}

```javascript
function main() {

    var value;

    setTimeout(function () {
        value = 'VALUE';
    }, 0 /* 0 ms. */);

    console.log(value); // ???

    setTimeout(function () {
        console.log(value); // ???
    }, 0);
    
    console.log(value); // ???

}

main();
```

{% endtab %}

{% tab title="👍" %}

```javascript
function main() {

    var value;

    setTimeout(function () {
        value = 'VALUE';
    }, 0 /* 0 ms. */);

    console.log(value); // 1 - undefined

    setTimeout(function () {
        console.log(value); // 3 - VALUE
    }, 0);
    
    console.log(value); // 2 - undefined
    
}

main();
```

{% endtab %}
{% endtabs %}

## Fonctionnement de l'Event Loop

1. **Inscription d'un listener** : Tous les traitements asynchrones nécessitent la définition d'une fonction de callback afin de récupérer le résultat du traitement ou simplement savoir que le traitement a abouti. Lors de cette étape, nous inscrivons explicitement ou indirectement un listener (notre callback) sur un événement. Exemples: `setTimeout`, `addEventListener` etc...&#x20;
2. **Ajout de la fonction à la queue** : Cette fonction de callback ne peut pas être appelée immédiatement dès réception du résultat car le thread est probablement occupé par l'exécution d'une autre fonction. Dans notre cas, nos deux fonctions de callback sont prêtes à être appelées mais le thread est occupé par l'exécution de notre fonction `main`. Le moteur JavaScript ajoute donc les fonctions de callback à la fin d'une "queue" de fonctions à appeler quand le thread sera libre.&#x20;
3. **Tick** : A la fin de l'exécution de la fonction `main`, le thread n'a pas le temps de s'ennuyer et va récupérer la fonction en tête de "queue" pour l'exécuter. Plus précisément, il s'agit ici de l'"event loop" qui est simplement la boucle infinie qui lance les fonctions les unes après les autres. **L'instant où l'"event loop" récupère une nouvelle fonction à exécuter s'appelle un "tick".**

![Event Loop](/files/-LAI7_xkST5mjYmLUpFw)

{% embed url="<https://www.youtube.com/watch?v=cCOL7MC4Pl0>" %}
Jake Archibald: In The Loop
{% endembed %}

{% embed url="<https://www.youtube.com/watch?v=8aGhZQkoFbQ>" %}
What the heck is the event loop anyway?
{% endembed %}


# Classes

## Création d'une classe

{% tabs %}
{% tab title="ES6 Class" %}

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    getName() {
        return this.firstName;
    }

}
```

{% endtab %}

{% tab title="Legacy Prototype" %}

```javascript
var Customer = function(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

Customer.prototype = {
    getName: function () {
        return this.firstName;
    }
}

```

{% endtab %}
{% endtabs %}

## Visibilité

En attendant la notion de [class fields](https://github.com/tc39/proposal-class-fields) qui sera probablement bientôt introduite en ECMAScript 2019 <http://kangax.github.io/compat-table/esnext/>, la notion de visibilité `private` se base sur la convention de nommage qui consiste à préfixer la propriété ou la méthode par le caractère underscore : `_`

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = null;
        this._isBadPayer = this._tellIfBadPayer();
    }
    
    getName() {
        return this.firstName;
    }
    
    _tellIfBadPayer() {
        return this.firstName === 'foo';
    }

}
```

## Propriétés

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
    }

    get firstName() {
        return this._firstName;
    }
    
    set firstName(value) {
        this._firstName = value;
    }
    
}

/* @HACK: Last time we use var, I promise! */
var customer = new Customer();

customer.firstName = 'Foo';

console.log(customer.firstName); // Foo
```

{% hint style="danger" %}
Ne les utilisez pas.

L'implémentation de propriétés peut s'avérer pratique dans certains cas extrêmes tels que l'intégration d'une librairie "legacy", mocking, décoration pour "type checking" etc...

Autrement, cela introduit surtout de l'ambiguité dans le code.

Qui pourrait imaginer que le code suivant puisse lever une exception ?

```javascript
var customer = new Customer();
element.textContent = customer.name;
```

Ou pire encore :

```javascript
/* @HACK: Do not remove this useless line as it initializes
 * the user eagerly instead of running it lazily. */
request.user;
```

*Toute ressemblance avec du code existant est fortuite.*
{% endhint %}

## Héritage

```javascript
export class WishtackProduct extends Product {

    ...

    getProductId() {
        return 'wishtack-' + this._wishtackId;
    }

}
```

{% hint style="warning" %}
Evitez l'héritage...

... et préférez la composition !
{% endhint %}

## Bonnes pratiques

{% hint style="success" %}
En l'absence de notion de "class fields", il est recommandé d'initialiser toutes les attributs dans le constructeur. Autrement, il est difficile de déterminer les attributs d'une classe et les attributs présents sur une instance dépendront alors des méthodes appelées.
{% endhint %}


# Hoisting is Dead: var vs. let vs. const

## Rappel

### Variables globales 🤮

```javascript
userName = 'Foo BAR';

console.log(userName); // Foo BAR
```

### Use strict 😅

```javascript
'use strict';

userName = 'Foo BAR'; // ReferenceError: userName is not defined
```

```javascript
'use strict';

console.log(userName); // ReferenceError: userName is not defined
```

## Hoisting

### Variable hoisting

{% tabs %}
{% tab title="🧐" %}

```javascript
'use strict';

console.log(userName); // ???

var userName = 'Foo BAR';
```

{% endtab %}

{% tab title="😱" %}

```javascript
'use strict';

console.log(userName); // undefined

var userName = 'Foo BAR';
```

{% endtab %}
{% endtabs %}

### Function hoisting

{% tabs %}
{% tab title="🧐" %}

```javascript
'use strict';

greetings(); // ???

function greetings() {
    console.log('HI!');
}

function greetings() {
    console.log('HELLO!');
}
```

{% endtab %}

{% tab title="😱" %}

```javascript
'use strict';

greetings(); // HELLO!

function greetings() {
    console.log('HI!');
}

function greetings() {
    console.log('HELLO!');
}
```

{% endtab %}
{% endtabs %}

### Un peu mieux

```javascript
'use strict';

greetings(); // TypeError: greetings is not a function.

var greetings = function () {
    console.log('HI!');
};

greetings(); // HI!

var greetings = function () {
    console.log('HELLO!');
};

greetings(); // HELLO!
```

## let

Les variables ne sont accessibles qu'après leur déclaration.

```javascript
console.log(userName); // ReferenceError: userName is not defined

let userName = 'Foo BAR';
```

Les variables ne sont accessibles que dans le bloc de code.

```javascript
if (true) {
    let userName = 'Foo BAR';
}

console.log(userName); // ReferenceError: userName is not defined
```

## const

`const` permet de déclarer des variables constantes qui ne peuvent pas être réinitialisées.

```javascript
const user = {
    firstName: 'Foo',
    lastName: 'BAR'
}

user = null; // TypeError: Assignment to constant variable.
```

{% hint style="warning" %}
`const` n'est pas immutable.

```javascript
user.firstName = 'John'; // OK!
```

{% endhint %}

{% hint style="success" %}
Il est recommandé de déclarer toutes les variables en `const` sauf quand la réutilisation d'une variable s'avère inévitable.

Cela évite de nombreuses erreurs d'inattention difficiles à diagnostiquer.

L'utilisation de const décourage le recyclage maladroit de variables :

```javascript
const user = {
    firstName: 'Foo',
    lastName: 'BAR'
}

/* 🤢*/
user = user.firstName; // TypeError: Assignment to constant variable.
```

{% endhint %}


# this & "binding"

Qui suis-je ?

{% tabs %}
{% tab title="🧐" %}

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    sayHi() {
        console.log('Hi ' + this.firstName);
    }
    
    sayHiLater() {
        setTimeout(function () {
            this.sayHi();
        }, 1000);
    }

}

const customer = new Customer('Foo', 'BAR');

customer.sayHiLater(); // ???
```

{% endtab %}

{% tab title="😱" %}

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    sayHi() {
        console.log('Hi ' + this.firstName);
    }
    
    sayHiLater() {
        setTimeout(function () {
            this.sayHi();
        }, 1000);
    }

}

const customer = new Customer('Foo', 'BAR');

customer.sayHiLater(); // TypeError: this.sayHi is not a function
```

{% endtab %}
{% endtabs %}

## Binding

La fonction de callback utilisée avec `setTimeout` n'est pas "bound" à notre instance de `Customer`. De plus, `setTimeout` espère nous aider en "bindant" l'objet timeout qu'il retourne à notre fonction de callback.

```javascript
const timeout = setTimeout(function () {
    console.log(this === timeout); // true
});
```

Pour lutter contre ce comportement... ~~m#@d!k~~... déstabilisant, nous pouvons "binder" notre instance de `Customer` à la fonction de callback.

### The hacky way

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    sayHi() {
        console.log('Hi ' + this.firstName);
    }
    
    sayHiLater() {
        setTimeout(function () {
            this.sayHi();
        }.bind(this), 1000);
    }

}
```

### The other hacky way

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    sayHi() {
        console.log('Hi ' + this.firstName);
    }
    
    sayHiLater() {
        const _this = this;
        setTimeout(function () {
            _this.sayHi();
        }, 1000);
    }

}
```

### Solution idéale

Pour la solution idéale, rendez-vous au [prochain chapitre](/ecmascript-6+/arrow-functions).


# Arrow Functions

```javascript
/* 90s */
function sayHi(userName) {
    console.log('Hi ' + userName);
}

/* 2000s */
var sayHi = function (userName) {
    console.log('Hi ' + userName);
};

/* 2015 */
const sayHi = (userName) => {
    console.log('Hi ' + userName);
};

/* 2018 */
export function sayHi(userName) {
    console.log('Hi ' + userName);
}
```

## No binding

Les arrow functions ne peuvent être "bound" et accèdent donc au `this` du closure parent.

```javascript
class Customer {

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    sayHi() {
        console.log('Hi ' + this.firstName);
    }
    
    sayHiLater() {
        setTimeout(() => {
            this.sayHi();
        }, 1000);
    }

}
```

## Exemple avec `Array.filter` et `Array.map`

```javascript
const productList = [
    {
        title: 'Browserstack',
        price: 50
    },
    {
        title: 'Keyboard',
        price: 20
    },
    {
        title: 'Prerender',
        price: 10
    }
];

const cheapProductList = productList.filter((product) => {
    return product.price < 25;
});

const cheapProductTitleList = cheapProductList.map((product) => {
    return product.title;
});

console.log(cheapProductTitleList); // ['Keyboard', 'Prerender']

```

## One-liner

Peu importe le contexte, les fonctions de callback sont souvent des "one-liners".\
Dans ce cas, les accolades, le `return` et le `;` peuvent être retirés.\
De même, si la fonction ne prend qu'un seul paramètre, les parenthèses peuvent être également ignorées.

On peut aussi remarquer le pattern builder des méthodes `filter` et `map` qui nous permet de chaîner les appels.

```javascript
const cheapProductTitleList = productList
    .filter(product => product.price < 25)
    .map(product => product.title);
```

{% hint style="warning" %}
En cas de shadowing de nom de variable, évitez les variables à une lettre ou des noms génériques.

`filter(u => u.id === user.id)`

`filter(it => it.id === user.id)`
{% endhint %}

{% hint style="success" %}
Il est commun de préfixer par un underscore \`\_\` les variables servant à itérer.

`filter(_user => _user.id === user.id`*`)`*
{% endhint %}


# Template Strings

### Exemple

```javascript
const appName = 'Wishtack';
const userName = 'Foo';
const greetings = `Hi ${userName},
Welcome to ${appName}!`

console.log(greetings);

// Result:
// Hi Foo,
// Welcome to Wishtack!
```

{% hint style="danger" %}

## Vulnérabilité Sécurité

N'utilisez jamais les template strings comme outil de templating HTML.\
Cela vous expose à des vulnérabilités de type XSS *(Cross-Site Scripting)*.

**Exemple :**

```javascript
/* userName is dynamically retrieved from malicious source:
 * query string, api, storage etc... */
const userName = '<img src=404 onerror=alert(1)>'; 
document.body.innerHTML = `<span>Hi ${userName}</span>`
```

{% endhint %}


# Syntactic Sugar


# Spread

## Array Spread

```javascript
const itemList = [1, 2, 3];
const additionalItemList = [5, 6];

const newItemList = [...itemList, 4, ...additionalItemList];

console.log(newItemList); // [1, 2, 3, 4, 5, 6]
```

## Object Spread

Très pratique pour respecter l'immutabilité.

```javascript
const user = {
    firstName: 'Foo',
    lastName: 'BAR',
    email: 'invalid@wishtack.com'
};

const newUser = {
    ...user,
    email: 'foo.bar@wishtack.com',
    phoneNumber: '+6 12 34 56 78'
};

console.log(newUser);
// {
//    firstName: 'Foo',
//    lastName: 'BAR',
//    email: 'foo.bar@wishtack.com',
//    phoneNumber: '+6 12 34 56 78'
//}
```

##


# Destructuring

## Array Destructuring

Très pratique pour les tests unitaires.

```javascript
const userList = [
    {firstName: 'Foo'},
    {firstName: 'John'}
];

const [user1, user2, user3, user4 = null] = userList;

console.log(user1); // { firstName: 'Foo' }
console.log(user2); // { firstName: 'John' }
console.log(user3); // undefined
console.log(user4); // null
```

## Object Destructuring

```javascript
const user = {
    firstName: 'Foo',
    lastName: 'BAR',
    email: 'foo.bar@wishtack.com'
};

const {firstName, lastName, phoneNumber} = user;

console.log(firstName); // Foo
console.log(lastName); // BAR
console.log(phoneNumber); // undefined
```

##


# Rest

## Array Rest

Les "rest parameters" sont l'équivalents des paramètres variadiques dans d'autres langages.

La fonction `add` ci-dessous peut prendre une infinité de paramètres qui seront mises dans l'`Array` `valueList`.

```javascript
const add = (...valueList) => {
    return valueList.reduce((total, value) => total + value, 0);
};

add(0, 1, 2, 3); // 6
```

{% hint style="warning" %}
Il vaut mieux éviter ce type d'usage du "rest".\
Cela réduit l'extensibilité de la fonction et il est préférable de prendre un paramètre de type `Array`.

```typescript
const add = (valueList) => {
    return valueList.reduce((total, value) => total + value, 0);
};

add([1, 2, 3]); // 6
```

{% endhint %}

En revanche, cela peut s'avérer très pratique dans des cas de wrapping ou de décoration etc...

```javascript
const wrapper = (funk, ...args) => {
    console.log(`Calling function with ${args.length} arguments.`);
    return funk(...args);
};

wrapper(add, 1, 2, 3);
```

## Object Rest

La même syntaxe peut être utilisée avec le "[destructuring](/ecmascript-6+/syntactic-sugar/destructuring#object-destructuring)" d'un objet :

```typescript
const user = {
    firstName: 'Foo',
    lastName: 'BAR',
    email: 'foo.bar@wishtack.com',
    phoneNumber: '123'
};

const { firstName, lastName, ...remainingProperties } = user;

console.log(remainingProperties); // { email: 'foo.bar@wishtack.com', phoneNumber: '123' }
```


# Object Literal Property Value Shorthand

Il arrive souvent que le nom d'une propriété d'un "Object Literal" porte le même nom que la variable qui y sera associée : `{firstName: firstName}`.

Cela produit de la redondance :

```javascript
const firstName = 'Foo';
const lastName = 'BAR';

const user = {
    firstName: firstName,
    lastName: lastName,
    email: 'foo.bar@wishtack.com'
};
```

... mais grâce à la syntaxe "Object Literal Property Value Shorthand", on peut alléger le code :

```javascript
const firstName = 'Foo';
const lastName = 'BAR';

const user = {
    firstName,
    lastName,
    email: 'foo.bar@wishtack.com'
};
```

{% hint style="info" %}
A vous de définir votre style guide à ce sujet dans votre équipe.

Si l'écosystème JavaScript est nouveau pour l'équipe, il vaut mieux éviter ce genre de raccourcis.
{% endhint %}

{% hint style="warning" %}
Méfiez-vous des éditeurs qui n'arrivent pas à "refactor" les "object shorthands".

Chez Wishtack, nous nous sommes interdits leur utilisation jusqu'au support de ce refactoring par IntelliJ / WebStorm.

![Object Literal Property Value Shorthand refactoring avec IntelliJ](/files/-LASKiNvzlOT9dTjo0iG)

![Object Literal Property Value Shorthand refactoring fail avec VSCode](/files/-LASLeu7uJdSP0BgS9l0)
{% endhint %}


# Named Parameters

Les paramètres ordonnées nuisent à la lisibilité du code et au refactoring.

```javascript
class Customer {
    constructor(firstName, lastName, email, phoneNumber) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.phoneNumber = phoneNumber;
    }
}

new Customer('Foo', null, null, '123');
```

Malheureusement, les named parameters n'existent pas mais une solution de contournement native est possible grâce au destructuring.

## Named Parameters avec un seul paramètre.

```javascript
class Customer {
    constructor(args) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
        this.email = args.email;
        this.phoneNumber = args.phoneNumber;
    }
}

new Customer({
    firstName: 'Foo',
    phoneNumber: '123'
});
```

... mais cette approche n'est pas très IDE-friendly. L'utilisateur de la classe ne verra pas clairement les paramètres attendus.

## Destructuring

```javascript
class Customer {
    constructor(args = {}) {
    
        const {firstName, lastName, email, phoneNumber = null} = args;
    
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.phoneNumber = phoneNumber;
    }
}
```

Hop ! On gagne l'initialisation des valeurs par défaut.

Ou encore mieux :

```javascript
class Customer {
    constructor({firstName, lastName, email, phoneNumber = null} = {}) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.phoneNumber = phoneNumber;
    }
}
```

## Recyclage

Pour des objets peu complexes, ce constructeur nous évite d'implémenter des "factories".

```javascript
const customer = new Customer({firstName: 'Foo'});

const customerFromJson = new Customer(JSON.parse(data));

const customerCopy = new Customer(customer);
```


# Compatibilité

Comment faire tant que toutes les fonctionnalités ne sont pas disponibles sur nos navigateurs cibles ?

​<https://kangax.github.io/compat-table/es6/>

{% embed url="<https://kangax.github.io/compat-table/es6/>" %}

## Transpilation

ECMAScript 5.1 est à l'ECMAScript moderne ce que l'assembleur est au C++.

Nous avons donc besoin de transpilateurs :

* Babel: <https://babeljs.io/>
* TypeScript: <https://www.typescriptlang.org/>

Mais cela n'est pas suffisant car la transpilation ne fait que convertir les nouvelles fonctionnalités syntaxiques.

## Polyfills

Nous avons besoin de compenser l'absence de certains objects, fonctions ou méthodes `customElements`, `fetch`, `Array.filter` sur certains navigateurs.

Pour remédier à cette limitation, nous utiliserons des librairies de polyfills. Ces librairies détectent l'absence des fonctionnalités et décident dans ce cas de les compenser avec une implémentation JavaScript.

Exemple :

```javascript
if (Array.prototype.first == null) {
    Array.prototype.first = function () {
        return this[0];
    };
}

const valueList = [1, 2, 3];

console.log(valueList.first()); // 1
```

L'une des librairies de polyfills les plus populaires est **core-js** <https://github.com/zloirock/core-js>.

Ou encore : [https://polyfill.io](https://polyfill.io/v2/docs/).


# TypeScript


# Pourquoi TypeScript ?

## Présentation de TypeScript

TypeScript est un langage gratuit et open-source développé et maintenu par Microsoft depuis octobre 2012.

TypeScript est une surcouche d'ECMAScript permettant l'ajout optionnel de typage statique.

Il n'existe actuellement pas de réel runtime TypeScript. Il faut utiliser un compilateur pour transpiler le code TypeScript en code ECMAScript valide que l'on peut ensuite exécuter sur le runtime JavaScript de notre choix : Browser, NodeJS etc...

TypeScript est un parfait compromis entre la flexibilité d'un langage dynamiquement typé et la rigueur d'un langage statiquement typé sans tomber dans la lourdeur syntaxique associée et ce grâce à d'intéressants concepts tels que le "duck typing" ou l'inférence de type.

TypeScript n'est pas un standard et aucun support n'est prévu sur les navigateurs, d'autant plus que contrairement à l'ECMAScript, TypeScript est typé statiquement et nécessitera donc toujours une transpilation.

## Feu AtScript

Initialement, Angular devait utiliser le langage AtScript prévu comme surcouche du TypeScript mais en Mars 2015, Microsoft annonce le support des fonctionnalités AtScript dans la prochaine version de TypeScript (1.5). AtScript a alors été abandonné avant sa naissance.

L'idée d'AtScript était d'introduire les annotations *(d'où le nom du langage)* qui ont finalement été introduits en TypeScript 1.5 sous le noms de "decorators" comme dans d'autres langages *(Python par exemple)*.&#x20;

![ECMAScript / TypeScript / AtScript](/files/-LAJ5lSIuqNSNrV0tKo5)


# De l'ECMAScript au TypeScript

L'ECMAScript est quasiment du TypeScript valide étant donné que le typage est optionnel.

Il est donc facile de migrer progressivement de l'ECMAScript vers le TypeScript.

## Les Propriétés

L'un des principaux changements empêchant l'ECMAScript d'être du TypeScript valide est la nécessité de déclarer les propriétés d'une classe.

En renommant simplement le fichier JavaScript en `.ts`, l'IDE commence à se plaindre.

![](/files/-LAJ7b2WhyAQDlfH6Zoj)

Le compilateur TypeScript (`tsc`) est également mécontent.

```bash
$ tsc customer.ts
customer.ts(11,18): error TS2339: Property 'firstName' does not exist on type 'Customer'.
customer.ts(12,18): error TS2339: Property 'lastName' does not exist on type 'Customer'.
```

Le minimum syndical est donc de déclarer ces propriétés.

```typescript
class Customer {

    firstName;
    lastName;

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

}
```

{% hint style="success" %}
La bonne pratique est de déclarer les propriétés avant le constructeur.
{% endhint %}


# Visibilité des Propriétés

{% hint style="info" %}
Par défaut, les propriétés sont publiques.
{% endhint %}

La visibilité des propriétés est contrôlée comme dans la plupart des langages objets statiquement typés :

```typescript
class Customer {
    public firstName: string;
    private _age: number;
}

const customer = new Customer();

// error TS2341: Property '_age' is private and only accessible within class 'Customer'.
console.log(customer._age);
```

{% hint style="success" %}
Bien que les conventions de nommage TypeScript et Angular découragent l'utilisation du préfixe underscore `_` pour indiquer qu'une propriété est privée, chez Wishtack, nous préfixons nos propriétés privées et vous le recommandons pour les raisons suivantes :

* Il est plus facile de déduire la visibilité d'une propriété sans lire le code source de la classe ou ouvrir un IDE. Exemple: `diff` de code ou code review.
* Vous constaterez plus facilement la fuite de propriétés privées dans des échanges JSON avec une API ou autre suite à une sérialisation maladroite.
* Tant que l'on ne build pas une application Angular avec l'AOT (en mode production), les propriétés utilisées dans les templates peuvent être private. Vous serez contents de dénicher rapidement les propriétés privées utilisées dans les templates grâce au préfixe plutôt que d'attendre l'echec du build sur votre intégration continue.

Dans tous les cas, l'important est d'être cohérent et homogène au sein d'une équipe.

P.S.: Angular et Angular Material préfixent également les propriétés privées par `_`.\
<https://github.com/angular/angular>\
<https://github.com/angular/material2>
{% endhint %}

N'oubliez pas que TypeScript analyse le code de façon statique et donc certaines peuvent lui échapper.\
C'est pour cette raison que le code suivant compile et s'exécute sans erreur.

```typescript
const customer = new Customer();
customer['_age'] = 30;
```


# Typing des Propriétés

## Typing des propriétés et des paramètres

```typescript
class Customer {

    firstName: string;
    lastName: string;
    age: number;

    constructor(firstName: string, lastName: string, age: number) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

}
```

Pour le code suivant :

```typescript
new Customer('Foo', 123);
```

... nous obtenons l'erreur :

```bash
error TS2345: Argument of type '123' is not assignable to parameter of type 'string'.
```

{% hint style="warning" %}
Par défaut, les paramètres et propriétés sont de type `any`.\
Ils sont alors compatibles avec tous les autres types.\
Si nous n'avions pas typé les paramètres du constructeur, nous aurions pu instancier l'objet avec des propriétés contenant des valeurs de type autre que `string`.

**Exemple** :\
Le code ci-dessous compile sans souci...

```typescript
class Customer {

    firstName: string;

    constructor(firstName) {
        this.firstName = firstName;
    }

}

const customer = new Customer(123);

customer.firstName.toUpperCase();
```

... mais les problèmes apparaîtront en runtime car `customer.firstName` sera de type `number` et n'aura donc pas de méthode `toUpperCase`.
{% endhint %}

## Raccourci pour les paramètres ordonnées du constructeur

La duplication du type en paramètre du constructeur et en propriété est fastidieuse et fréquente.

TypeScript propose une syntaxe concise "**Parameter Properties**" pour déclarer et initialiser les propriétés en indiquant simplement la visibilité de chaque paramètre du constructeur.

```typescript
class Customer {

    constructor(public firstName: string,
                public lastName: string,
                public age: number) {
    }

}

const customer = new Customer('Foo', 'BAR', 30);

console.log(customer.firstName); // Foo
```


# Types

## Les types les plus fréquents

### boolean

```typescript
let value: boolean;
value = true;
```

### number

```typescript
let value: number;
value = 10;
value = 10.3;
value = Infinity;
value = NaN;
```

### string

```typescript
let value: string;
value = 'Foo BAR';
```

### array

```typescript
let value: string[];

value = ['Angular', 'Python'];

value.push(42); // error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
```

### enum

```typescript
enum Role {
    Coder,
    Developer
}

let role: Role;

role = Role.Developer;

console.log(role); // 1
console.log(Role[role]); // Developer
```

{% hint style="danger" %}
Comme dans de nombreux langages, il est préférable d'éviter les enums à auto-incrément pour les raisons suivantes :

* Ce type d'enums décourage le refactoring car il est nécessaire de "rebuild" toutes les applications et librairies qui en dépendent. *(On revient aux problèmes de compatibilité binaire etc...)*
* Le debug est moins pratique.
* Il faut absolument passer par un serializer/deserializer pour communiquer la valeur avec d'autres applications, services etc...
  {% endhint %}

### string enum

```typescript
enum Role {
    Coder = 'coder',
    Developer = 'developer'
}

let role: Role;

role = Role.Developer;

console.log(role); // developer
```

### Number, String, Boolean and Object

{% hint style="danger" %}
N'utilisez jamais les types suivants : **Number**, **String**, **Boolean** et **Object**.

Ce ne sont pas les types primitifs. Considérez-les comme "legacy".\
\
Au lieu de `Object`, utilisez le type TypeScript `object`.
{% endhint %}

## Paramètres optionnels

Contrairement à l'ECMAScript où tous les paramètres sont considérés optionnels, en TypeScript, les paramètres doivent être explicitement indiqués comme optionnels avec la syntaxe suivante :

```typescript
const sendMessage = (email: string, message: string, subject?: string) => {
    console.log(`${email}: [${subject}] ${message}`);
};

sendMessage(); // error TS2554: Expected 2-3 arguments, but got 0.

sendMessage('contact@wishtack.com', 'Help!'); // contact@wishtack.com: [undefined] Help!
sendMessage('contact@wishtack.com', 'Help!', 'Coaching'); // contact@wishtack.com: [Coaching] Help!
```

... ou en spécifiant une valeur par défaut :

```typescript
const sendMessage = (email: string, message: string, subject: string = null) => {
    console.log(`${email}: [${subject}] ${message}`);
};

sendMessage('contact@wishtack.com', 'Help!'); // contact@wishtack.com: [null] Help!
```


# Interfaces

Une interface TypeScript permet de définir la signature *(ou le contrat)* d'une classe où même une fonction.

Les interfaces TypeScript ne sont utilisées que lors de la transpilation. Elle disparaissent totalement en runtime étant donné qu'elles ne contiennent pas de code.

## Class interface

```typescript
interface Serializable {
    serialize(): string;
}

class Customer implements Serializable {

    firstName: string;
    
    serialize() {
        return this.firstName;
    }
    
}

class Cache {

    setItem(key: string, value: Serializable) {
        ...
    }

}

const cache = new Cache();

cache.setItem('current-user', new Customer());
```

{% hint style="success" %}
Les "styles guides" TypeScript et Angular découragent l'utilisation du préfixe `I` pour les interfaces.

Cela est principalement lié au deux raisons suivantes :

* Au fil des refactorings, les classes deviennent des interfaces avec plusieurs implémentations.
* Grâce à l'injection de dépendance, nous travaillerons la plupart du temps avec des interfaces. Le code perd alors en lisibilité si tous les types sont préfixés par des `I`.
  {% endhint %}

## Function interface

Il est également possible de définir des interfaces pour les fonctions.

Très pratique dans un environnement asynchrone où il pleut de la "callback".

```typescript
class Product {
    name: string;
    price: number;
}

interface ProductFilterCallback {
    (product: Product): boolean
}

class ProductRepository {

    getMatchingItemList(filter: ProductFilterCallback) {
        ...
    }

}

const productRepository = new ProductRepository();

productRepository.getMatchingItemList(product => product.price < 100);

// error TS2345: Argument of type '(product: Product) => number' is not assignable to parameter of type 'ProductFilterCallback'.
//  Type 'number' is not assignable to type 'boolean'.
productRepository.getMatchingItemList(product => product.price);
```

Fini le reverse engineering ou ça :

```typescript
doSomething(data => {
    console.log(data); // 🤞
});
```


# Inference

L'inférence de type est l'un des points les plus forts et les plus sous-estimés de TypeScript.\
Il s'agit ici de **déduire implicitement le typage** et **faire gagner du temps de développement et de refactoring sans perdre en rigueur**.

L'idée de TypeScript est en quelque sorte de typer au minimum et de laisser le "transpiler" gérer le reste.

```typescript
let userName = 'Foo';

userName = 10; // error TS2322: Type '10' is not assignable to type 'string'.
```

Le type de  la variable `userName` est donc déduit à l'initialisation de la variable et il n'est donc pas nécessaire de la typer explicitement.

## Quelques exemples

```typescript
const getWeather = (city) => {

    if (city == null) {
        throw new Error(`D'OH!`);
    }

    return {
        rainProbability: 0,
        temperature: 30
    };
};

let weather = getWeather('Lyon');

// error TS2322: Type '"🔥 Wishtack is cool ! 🔥"' is not assignable
// to type '{ rainProbability: number; temperature: number; }'.
weather = '🔥 Wishtack is cool ! 🔥';
```

... ou encore :

```typescript
const getWeather = (city) => {

    if (city == null) {
        throw new Error(`D'OH!`);
    }

    return {
        rainProbability: 0,
        temperature: 30
    };
};

const getCityWeather = (city: string) => {

    const result = getWeather(city);

    return {
        ...result,
        city
    };

};

let cityWeather = getCityWeather('Lyon');

// error TS2322: Type '"🔥 Wishtack is cool ! 🔥"' is not assignable
// to type '{ city: string; rainProbability: number; temperature: number; }'.
cityWeather = '🔥 Wishtack is cool ! 🔥';
```

... au lieu de :

{% hint style="danger" %}

```typescript
interface Weather {
    rainProbability: number;
    temperature: number;
}

const getWeather = (city: string): Weather => {

    if (city == null) {
        throw new Error(`D'OH!`);
    }

    return {
        rainProbability: 0,
        temperature: 30
    };
};

interface CityWeather extends Weather {
    city: string;
}

const getCityWeather = (city: string): CityWeather => {

    const result: Weather = getWeather(city);

    return {
        ...result,
        city
    };

};

let cityWeather: CityWeather = getCityWeather('Lyon');

cityWeather = '🔥 Wishtack is hot ! 🔥';
```

{% endhint %}

... ou :

```typescript
const productList = [
    {
        title: 'Browserstack',
        price: 50
    },
    {
        title: 'Keyboard',
        price: 20
    },
    {
        title: 'Prerender',
        price: 10
    }
];

let cheapProductsTotalPrice = productList
    .filter(product => product.price < 25)
    .map(product => product.price)
    .reduce((total, price) => total + price, 0);

// error TS2322: Type '"Oups!"' is not assignable to type 'number'.
cheapProductsTotalPrice = 'Oups!';
```

... au lieu de :

{% hint style="danger" %}

```typescript
interface Product {
    title: string;
    price: number;
}

const productList: Product[] = [
    {
        title: 'Browserstack',
        price: 50
    },
    {
        title: 'Keyboard',
        price: 20
    },
    {
        title: 'Prerender',
        price: 10
    }
];

let cheapProductsTotalPrice = productList
    .filter((product: Product): boolean => product.price < 25)
    .map<number>((product: Product): number => product.price)
    .reduce((total: number, price: number): number => total + price, 0);

cheapProductsTotalPrice = 'Oups!';
```

{% endhint %}

## Inférence et callbacks

Dans le dernier exemple du [chapitre sur les interfaces](/typescript/interfaces#function-interface), nous avons implicitement profiter de l'inférence de type.

Toute la puissance de TypeScript repose sur cet art de typer le moins possible mais au bon endroit pour en profiter au maximum.

{% hint style="success" %}
La signature des fonctions de "callback" que vous attendez est l'un des éléments les plus stratégiques à typer.
{% endhint %}

```typescript
interface Product {
    title: string;
    price: number;
}

interface ProductFilterCallback {
    (product: Product): boolean
}​

class ProductRepository {
​
    getMatchingItemList(filter: ProductFilterCallback) {
    }
​
}

const productRepository = new ProductRepository();

// error TS2345: Argument of type '(productName: string) => true' is
// not assignable to parameter of type 'ProductFilterCallback'.
//     Types of parameters 'productName' and 'product' are incompatible.
//     Type 'Product' is not assignable to type 'string'.
productRepository.getMatchingItemList((productName: string) => {
    return true;
});

// error TS2345: Argument of type '(product: Product) => string' is
// not assignable to parameter of type 'ProductFilterCallback'
productRepository.getMatchingItemList(product => {
    return 'not boolean';
});

// error TS2339: Property 'name' does not exist on type 'Product'.
productRepository.getMatchingItemList(product => {
    return product.name != null;
});
```

## Et si votre IDE est sympa

![TypeScript type inference + autocomplete](/files/-LAOH8Si4q7U5HXP_zRf)

![TypeScript type inference + refactoring](/files/-LAOHH6zkgNth-XsrnVl)

![TypeScript type inference + refactoring error detection](/files/-LAOHLJ-Ue7G_yYdOtDa)


# Duck Typing

On retrouve en TypeScript ce concept très pythonique et pratique.

> If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

... ce à quoi nous pouvons ajouter que si ce n'était pas un canard alors il n'avait qu'à **mieux marquer sa différence** et surtout **ne pas rôder autour des chasseurs**.&#x20;

Autrement dit, si deux objets "matchent" en terme de "duck typing" alors que leurs propriétés n'ont pas du tout la même signification alors cela révèle plutôt un problème de design qu'un problème lié au "duck typing" en lui-même.

Exemple :

```typescript
/* Some user library. */
class User {
    firstName: string;
    lastName: string;
    email: string;
    address: string;
}

/* Some shop administration library. */
class ShopOwner {
    firstName: string;
    lastName: string;
    email: string;
    phoneNumber: string;
}

class Shop {
    email: string;
}

/* Emailing library. */
interface Emailable {
    firstName: string;
    lastName: string;
    email: string;
}

const sendEmail = (message: string, emailable: Emailable) => {
    ...
};

// OK
sendEmail('Hi', new User());
// OK
sendEmail('Hi', new ShopOwner());
// OK
sendEmail('Hi', {
    firstName: 'Foo',
    lastName: 'BAR',
    email: 'foo.bar@wishtack.com'
});

// error TS2345: Argument of type 'Shop' is not assignable to parameter of type 'Emailable'.
//  Property 'firstName' is missing in type 'Shop'.
sendEmail('Hi', new Shop());
```


# Duck Typing Patterns


# Compatibilité de Librairies

ECMAScript6 a introduit les "promises" dont nous verrons le fonctionnement plus tard.\
L'idée globale des "promises" est de retourner de façon synchrone un "handler" sur lequel l'appelant s'inscrit (grâce à la méthode `then`) pour être informé quand le traitement asynchrone est terminé.

```typescript
const promise = ...;

promise.then(data => {
    console.log(`Woohoo! Data is finally ready: ${data}`);
});
```

En attendant ECMAScript 6, de nombreuses librairies ont dû produire leur propre implémentation des `Promise` : [kriskowal's q](https://github.com/kriskowal/q) / [AngularJS](https://docs.angularjs.org/api/ng/service/$q) / [jQuery](https://api.jquery.com/jquery.deferred/) / [Bluebird](http://bluebirdjs.com/docs/getting-started.html)...

Le problème des promises est qu'elles ont tendance à se propager dans les applications et à travers les librairies.

Dans une rigueur extrême de langage statiquement typé sans Duck Typing, il faudrait que toutes ces librairies s'accordent à implémenter une interface universelle commune... C'est une utopie dans un univers où la durée de vie moyenne d'une librairie est de quelques mois.

Modulo quelques variations, toutes les implémentations de "promises" implémentent au moins la méthode `then` (et très souvent `catch`).\
Grâce au Duck Typing, l'interface suivante est compatible avec toutes les implémentations :

```typescript
interface Thenable {
    then(callback): Thenable;
}
```


# Entity Constructor

Essayons d'appliquer les [named parameters](/ecmascript-6+/named-parameters) au constructeur d'une entité TypeScript.

```typescript
class Customer {

    firstName: string;
    lastName: string;
    
    constructor(args) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }

}
```

## Paramètre `args` optionnel

Le paramètre `args` est obligatoire.

Il suffit de lui indiquer une valeur par défaut.

En utilisant `args?` ou `args = null` nous obtiendrions une exception lors de l'accès à la propriété `firstName`. Il faut donc initialiser à `{}`.

```typescript
class Customer {

    firstName: string;
    lastName: string;

    constructor(args = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }

}
```

## Typing du paramètre `args`

Le paramètre `args` n'est pas typé et notre IDE ne nous aidera pas.

Il faut typer le paramètre `args`.

```typescript
interface CustomerArgs {
    firstName: string;
    lastName: string;
}

class Customer {

    firstName: string;
    lastName: string;

    constructor(args: CustomerArgs = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }

}
```

## Valeurs par défaut

La valeur par défaut `{}` ne correspond pas au type `CustomArgs`. Il suffit de créer une constante avec les valeurs par défaut ou plus simplement marquer toutes les propriétés comme optionnelles.

```typescript
interface CustomerArgs {
    firstName?: string;
    lastName?: string;
}

class Customer {

    firstName: string;
    lastName: string;

    constructor(args: CustomerArgs = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }

}
```

## Le Duck Typing rentre en jeu

`Customer` et `CustomerArgs` sont similaires au sens [Duck Typing](/typescript/duck-typing).

```typescript
class Customer {

    firstName?: string;
    lastName?: string;

    constructor(args: Customer = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }

}
```

## Une "factory" gratuite

Grâce au Duck Typing, nous obtenons une "factory" gratuitement :

```typescript
/* Default. */
customer = new Customer();

/* Partial. */
customer = new Customer({
    firstName: 'Foo'
});

/* Deserializer. */
const data = JSON.parse('...');
// Instead of new Customer(data.firstName, data.lastName)
customer = new Customer(data); 

/* Copy. */
customer = new Customer(customer);

// error TS2345: Argument of type '{ firstName: string; lastName: string; email: string; }' is not assignable to parameter of type 'Customer'.
//  Object literal may only specify known properties, and 'email' does not exist in type 'Customer'.
customer = new Customer({
    firstName: 'Foo',
    lastName: 'BAR',
    email: 'foo.bar@wishtack.com'
});
```

## Entity schema

Si on change la signature de la classe `Customer`, paf ! ça casse tout !

```typescript
class Customer {

    firstName?: string;
    lastName?: string;

    // error TS2322: Type '{}' is not assignable to type 'Customer'.
    //   Property 'getName' is missing in type '{}'.
    constructor(args: Customer = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }
    
    getName() {
        return `${this.firstName} ${this.lastName}`;
    }

}
```

Dans ce cas, la solution ne nécessite qu'un léger refactoring local en séparant la description de l'entité avec le constructeur dans la classe `CustomerSchema` puis les méthodes dans la classe `Customer`.

```typescript
class CustomerSchema {

    firstName?: string;
    lastName?: string;

    // error TS2322: Type '{}' is not assignable to type 'Customer'.
    //   Property 'getName' is missing in type '{}'.
    constructor(args: CustomerSchema = {}) {
        this.firstName = args.firstName;
        this.lastName = args.lastName;
    }
    
}

class Customer extends CustomerSchema {
    
    getName() {
        return `${this.firstName} ${this.lastName}`;
    }

}
```

{% hint style="success" %}
Dans une approche plus fonctionnelle et avec un souci de séparation des responsabilités, la méthode `Customer.getName` pourrait être déplacée dans une classe `CustomerHelper:`

```typescript
class CustomerHelper {
    getName(customer: Customer) {
        ...
    }
}
```

{% endhint %}


# Décorateurs

Les décorateurs permettent par simple annotation *(i.e.:*  `@magic() prop: string;`*)*  de modifier le comportement d'une classe, d'une propriété ou d'une fonction.

Le rôle des décorateurs est de factoriser certains "patterns" en surchargeant le comportement natif.

Cette fonctionnalité pythonique est prévue dans les versions futures d'ECMAScript *(*[*https://tc39.github.io/proposal-decorators/*](https://tc39.github.io/proposal-decorators/)*)* mais étant donné qu'Angular en avait besoin *(d'où l'idée de l'AtScript)*, TypeScript l'a implémentée en avance.


# Décorateurs de Propriété

Le code ci-dessous implémente et utilise un décorateur `ReadOnly` qui surcharge l'implémentation du "setter" de la propriété et déclenche automatiquement une exception en runtime.

`ReadOnly` est une factory qui retourne un décorateur.

Le décorateur prend deux paramètres :

* `target` : la référence de la classe.
* `key` : le nom de la propriété.

```typescript
const ReadOnly = () => {
    
    /* Return the decorator. */
    return (target, key: string) => {
        Object.defineProperty(target, key, {
            set: () => {
                throw new Error(`${target.constructor.name}.${key} is read only`);
            }
        });
    };
    
};

class Customer {
    @ReadOnly() firstName: string;
}

const customer = new Customer();
customer.firstName = 'Foo';
```

Depuis la version 2.0 de TypeScript, il est désormais possible d'indiquer si une propriété est `readonly` avec la syntaxe native suivante :

```typescript
class Customer {
    readonly firstName: string;
}
```

{% hint style="warning" %}
Il faut bien noter la différence entre les deux.

La visibilité readonly n'est analysée que statiquement et peut donner une fausse impression de confiance.

En revanche, le décorateur est exécuté en runtime et il faudra penser à le désactiver en production pour éviter son coût d'exécution.
{% endhint %}


# Décorateurs de Classe

## Constructor tracker

`Track` est une factory qui retourne un "decorator".

Le décorateur prend en paramètre`target`, la référence de la classe et retourne une nouvelle classe.

```typescript
const Track = () => {

    return (target) => {

        const Klass = function (...args) {
            console.log(`new ${target.name}(${args})`);
            target.constructor.apply(this, args);
        };

        Object.assign(Klass.prototype, target.prototype);

        return Klass as any;

    };

};

@Track()
class Customer {

    constructor(public firstName?: string) {
    }

}

let customer = new Customer(); // new Customer()
customer = new Customer('Foo'); // new Customer(Foo)
```

## Custom element decorator

Cet exemple de factory prend des paramètres permettant de personnaliser le décorateur.

```typescript
const CustomElement = ({selector, template}) => (target) => {

    class Element extends HTMLElement {
    }

    Object.assign(Element.prototype, {
        connectedCallback() {
            this.innerHTML = template;
            return target.connectedCallback.bind(this)();
        }
    });

    customElements.define(selector, Element);

    return Element as any;

};

@CustomElement({
    selector: 'wt-angular-guide',
    template: `<a href="https://angular-guide.wishtack.io">Angular Guide</a>`
})
class AngularGuide {

    connectedCallback() {
        console.log('YEAY!');
    }

}
```

Essayez :

```bash
tsc playground.ts --experimentalDecorators --target es2015
```

Puis copier le résultat JavaScript dans la console de votre navigateur et insérer des éléments `<wt-angular-guide>` sur votre page.


# Décorateurs de Méthode & Paramètres

Un décorateur de méthode permet d'en modifier le comportement par "wrapping".

Le décorateur en exemple ci-dessous n'opère aucun changement.

```typescript
const Noop = () => (target, key: string) => {
    return target[key];
};

class Calculator {

    @Noop()
    sum(a, b) {
        console.log('computing...');
        return a + b;
    }

}

const calculator = new Calculator();
```

{% hint style="warning" %}
Remarquez le pattern de "currying" très fréquent dans l'implémentation de décorateurs.

La syntaxe suivante :

```typescript
const Noop = () => {
    return (target, key) => {
        return target[key];
    };
}
```

... est identique à celle-ci :

```typescript
const Noop = () => (target, key) => {
    return target[key];
};
```

A consommer avec modération.
{% endhint %}

Les décorateurs de paramètres permettent principalement d'ajouter des metadata à la classe pour que les décorateurs de méthode puissent s'en servir.

## Mémorisation des résultats

Le décorateur ci-dessous construit progressivement un objet de mémorisation permettant de "mapper" les paramètres au dernier résultat obtenu afin d'éviter de refaire le même calcul inutilement.

```typescript
const Memoize = () => (target, key: string) => {

    const memory = {};

    const original = target[key];

    target[key] = function (...args) {

        /* Retrieve last returned value from memory if available. */
        const value = memory[args.toString()];

        if (value !== undefined) {
            return value;
        }

        const result = original.apply(this, args);

        memory[args.toString()] = result;

        return result;

    };

};

class Calculator {

    @Memoize()
    sum(a, b) {
        console.log('computing...');
        return a + b;
    }

}

const calculator = new Calculator();

console.log(calculator.sum(1, 2));
// computing...
// 3
console.log(calculator.sum(1, 2));
// 3
console.log(calculator.sum(1, 2));
// 3
console.log(calculator.sum(2, 2));
// computing...
// 4
console.log(calculator.sum(1, 2));
// 3
```

## Contract checking en runtime

```typescript
import 'reflect-metadata';

const _contractDictMetadataKey = '__contractDict';

const ApplyContracts = () => (target, key: string) => {

    const originalMethod = target[key];
    const contractDict = Reflect.getOwnMetadata(_contractDictMetadataKey, target, key);

    target[key] = function (...argList) {

        if (contractDict !== undefined) {

            argList.forEach((value, index) => {

                const contract = contractDict[index];

                if (contract === undefined) {
                    return;
                }

                if (!contract(value)) {
                    throw new Error(`Value '${value}' does not respect contract for \`${target.constructor.name}.${key}(param_${index})\`.`);
                }

            });

        }

        return originalMethod(...argList);

    }

};

const Contract = (contract: (value: any) => boolean) => (target, key, index): any => {

    let contractDict = Reflect.getOwnMetadata(_contractDictMetadataKey, target, key);

    if (contractDict === undefined) {
        contractDict = {};
    }

    contractDict[index] = contract;

    Reflect.defineMetadata(_contractDictMetadataKey, contractDict, target, key);

};

const isPositive = value => value > 0;

class Utils {

    @ApplyContracts()
    double(@Contract(isPositive) number: number): number {
        return number * 2;
    }

}

console.log(new Utils().double(2)); // 4

// Error: Value '0' does not respect contract for `Utils.double(param_0)`.
console.log(new Utils().double(0));                
```


# Quelques Liens

## TypeScript Documentation

<https://www.typescriptlang.org/docs/home.html>


# Tools


# Clavier mécanique

![QWERTY Mechanical Keyboard](/files/-Ldr_4KET8HtZCdXvilI)

{% embed url="<https://www.amazon.fr/gp/product/B01EJ7ZHQG/ref=as_li_tl?ie=UTF8&tag=wishtack-21>" %}


# Git

Git est un outil de versioning et de développement collaboratif *(ou V.C.S. : Version Control System)*. Il est devenu le standard du domaine grâce à son fonctionnement décentralisé, la facilité de gestion des branches et l'automatisation grâce à une multitude d'outils comme [github.com](https://github.com/), [gitlab.com](https://gitlab.com), [git flow](https://github.com/nvie/gitflow), [travis](https://travis-ci.org/)...

* Vous pouvez télécharger git ici <https://git-scm.com/download>.

Par défaut, les projets Angular sont initialisés sur un "repository" git mais vous pouvez utiliser un autre *Version Control System*.\
Toutefois, avant de vous y aventurer, n'oubliez pas que **vos cas particuliers ne sont probablement pas assez particuliers pour mériter un traitement particulier** et que **vos traitements particuliers provoqueront des cas particuliers...**


# Command Line

Les outils de développement JavaScript sont très orientés ligne de commande.

L'avantage de cette approche est la facilité d'automatisation et l'extensibilité *(e.g. : intégration continue, déploiement continu...)*.


# NodeJS

Bien que NodeJS ne soit pas initialement destiné au développement d'applications Front-End, la plupart des outils de développement se basent dessus. Il n'y a rien de mieux qu'un moteur JavaScript pour parser, transformer et générer du JavaScript.

Il est clairement inévitable d'utiliser NodeJS pour le développement Front-End.\
NodeJS doit être disponible dans vos workflows d'intégration continue.

* Vous pouvez télécharger NodeJS ici : <https://nodejs.org/en/>

La version current fonctionne très bien mais pour les plus prudents, vous pouvez utiliser la version LTS *(Long Term Support)*. Seules les versions au numéros pairs sont en LTS.


# NPM

**NPM (Node Package Manager)** comme son nom l'indique est le "package manager" officiel de l'univers JavaScript *(frontend / backend)*. Il est installé automatiquement lors de l'installation de NodeJS.

> Pour ceux qui ont connu, [bower](https://bower.io/) est mort.

NPM permet entre autres :

* D'installer des modules globalement *(un peu comme* `apt` *ou* `yum` *pour les linuxiens).*
* De créer un module JavaScript et le publier.
* D'installer les dépendances d'un module à partir d'un fichier de description de dépendances.
* De mettre à disposition des développeurs *(et de l'intégration continue)* un point d'entrée pour "build", "debug", "deploy" ou "test" votre application.

Nous n'utiliserons NPM qu'une seule fois, pour installer [Yarn](/tools/yarn).

```bash
npm install --global yarn
```

Rendez-vous au [chapitre suivant](/tools/yarn) pour plus d'explications.


# Yarn

En 2016, face à l'instabilité, l'imprédictibilité, la lenteur, l'indéterminisme et le manque de sécurité d'NPM 💥, Facebook décide de publier en open-source une alternative à NPM nommée **fbkpm**.

En quelques mois, **fbkpm** devient **kpm** puis finalement **Yarn.**\
Yarn atteint alors très rapidement un bon niveau de stabilité puis une forte adoption par la communauté JavaScript *(fortement déçue par NPM)* début 2017 avant même de passer en version 1.0.0 plus tard dans l'année.

Yarn a beaucoup inspiré les dernières versions d'NPM qui progresse tranquillement en suivant les traces laissées par Yarn 🐈.

La liste des modules disponibles est accessible sur [https://yarnpkg.com](https://yarnpkg.com/) et <https://www.npmjs.com>.

{% hint style="info" %}
Nous vous recommandons d'utiliser yarnpkg.com dont les performances sont impressionnantes grâce à [Algolia](https://www.algolia.com/).

Essayez par vous même !
{% endhint %}

## Installation Yarn

{% hint style="danger" %}
Sur windows... <https://yarnpkg.com/en/docs/install#windows-stable>
{% endhint %}

... ou sur un système qui fonctionne :

```bash
npm install --global yarn
```


# Pourquoi Yarn ?

## Stabilité

En cas d'anomalie *(interruption lors de l'installation par exemple)*, vos dépendances seront toujours dans un état stable et il suffit de relancer une commande pour reprendre l'installation.

Avec NPM, il arrivait souvent qu'une interruption corrompe les dépendances. Il fallait alors supprimer toutes les dépendances et relancer l'installation... parfois après quelques heures de diagnostic.

## Sécurité

Malheureusement, les modules publiés sur NPM registry (<https://www.npmjs.com/>) ne sont pas signés.

Yarn intègre une solution "best effort" qui consiste à retenir les "checksum" des modules téléchargés. Ainsi, si jamais le contenu du module est modifié par malveillance ou par erreur, Yarn pourra détecter l'anomalie... à condition d'avoir récupéré ce module avec la même version précédemment.

## Résilience et mode "offline"

Si une dépendance est déjà disponible dans le cache de votre machine, elle sera utilisée par Yarn sans déclencher aucune connexion vers l'extérieur.

En cas d'erreur réseau, Yarn réessaie plusieurs fois tout en continuant l'installation des autres dépendances.

## Déterminisme

Un ancien problème très commun avec NPM est que les versions des dépendances indirectes *(ou sous-dépendances)* ne sont pas maitrisées. A deux moments différents de la journée, deux développeurs pouvaient récupérer le même code source, installer les dépendances et obtenir des résultats différents.

Pour remédier à ce problème, à chaque installation de dépendances, Yarn génère un fichier `yarn.lock`  *(une sorte de snapshot de toute votre arborescence de dépendances)* qu'il faut "commit" pour partager avec les autres développeurs et les outils. Lorsqu'on exécute la commande `yarn` ou `yarn install`, Yarn installe les dépendances directes et indirectes en se basant sur les informations du fichier `yarn.lock`.

Pour mettre à jour les dépendances, il suffit de lancer la commande `yarn upgrade` et le fichier `yarn.lock` sera mis à jour avec les dernières dépendances.

Avant l'apparition de Yarn, NPM proposait la commande `npm shrinkwrap` (<https://docs.npmjs.com/cli/shrinkwrap>) mais le résultat n'était pas déterministe.

NPM génère désormais *(depuis la version 5)* un fichier `package-lock.json` équivalent au `yarn.lock`.

## Performance

Grâce à la parallélisation des téléchargements, le pool de connexions, le caching etc... Yarn est capable d'installer très rapidement l'ensemble des dépendances *(bien plus vite qu'NPM...)*.


# Définition et Installation des Dépendances

Un module JavaScript *(ou une application Angular)* définit toujours un fichier `package.json` à sa racine.

## `yarn init`

Si ce fichier n'existe pas, vous pouvez le créer avec la commande `yarn init`.

## `package.json` : `dependencies`

La section `dependencies` du fichier `package.json` permet de définir la liste des dépendances de votre application :

```javascript
{
    ...
    "dependencies": {
        "core-js": "^2.4.1",
        "rxjs": "~5.5.10"
    }
    ...
}
```

## `yarn install`

En lançant la commande `yarn install` *(ou simplement* `yarn`*)*, Yarn procédera ainsi :

1. Il vérifie la présence du fichier `yarn.lock`.\
   Si le fichier est présent, Yarn installera exactement les dépendances et sous-dépendances indiquées dans ce fichier.
2. Si la dépendance vient d'être ajoutée dans le fichier `package.json` ou si le fichier `yarn.lock` est absent *(première installation)*, il recherche la version la plus récente correspondant au critère indiqué :\
   **`^2.4.1`** => **`>=2.4.1 & <3.0.0`**\
   **`~5.5.10`** => **`>=5.5.10 & <5.6.0`**
3. Il indique la version sélectionnée et installée dans le fichier `yarn.lock`.

{% hint style="info" %}
Les dépendances sont installées dans le dossier local `node_modules` qu'il ne faut jamais "commit".
{% endhint %}

{% hint style="success" %}
Pensez à toujours "commit" le fichier yarn.lock et à lancer la commande yarn install, à chaque fois que vous mettez à jour votre code source.&#x20;
{% endhint %}

## `yarn add`

Pour ajouter une dépendance à votre application, il suffit de lancer la commande `yarn add` en indiquant les modules que vous souhaitez installer :

```bash
yarn add core-js rxjs rest-cache
```

Vous pouvez également indiquer la version souhaitée :

```bash
yarn add core-js rxjs@~5.5.10 rest-cache@next
```

... ou via l'IDE 😉 :

![ Ajout d'une dépendance depuis l'IDE](/files/-LAXCzUUyVU6Bh4lAM3g)

## `package.json` : `devDependencies`

Vous remarquerez rapidement la présence d'autres dépendances dans le champ `devDependencies` du fichier `package.json`.

Ces dépendances sont installées de la même façon que celles de la section `dependencies` sauf si la variable d'environnement `NODE_ENV` vaut `production` :

```bash
export NODE_ENV=production
yarn install # dev dependencies won't be installed
```

Cela sert surtout aux applications NodeJS afin d'éviter d'installer inutilement les outils de développement *(build, automation et testing etc...)* en production.

Dans le cas des applications frontend, il s'avère qu'après le "build" de notre application, nous n'aurons plus besoin d'aucune dépendance.

{% hint style="success" %}
La convention est de mettre :

* dans `dependencies`, toutes les dépendances dont une partie importante finira dans le résultat du build *(e.g. : @angular/core, core-js, rxjs)*.<br>
* et dans les `devDependencies`, toutes les dépendances utilisées pour les tâches de build, automation et testing *(e.g. : @angular/cli, jasmine, karma, protractor, typescript)*.
  {% endhint %}

Pour ajouter des dépendances dans cette section, il suffit d'ajouter l'option `--dev` à la commande `yarn add`.

```bash
yarn add --dev karma
```


# Scripts

## Comment ?

La section `scripts` du fichier `package.json` permet tout simplement de définir des tâches utilisées par les développeurs ou pour l'automatisation *(intégration continue etc...).*

```javascript
{
    "scripts": {
        "deploy": "tools/deploy.sh",
        "hello": "cowsay 👋",
        "start": "webpack-dev-server"
    },
    "devDependencies": {
        "cowsay": "*",
        "webpack-dev-server": "*"
    }
}
```

Il suffit ensuite d'exécuter yarn en lui indiquant le script à exécuter :

```bash
yarn hello
```

et on obtient le résultat suivant :

```bash
yarn run v1.6.0
$ cowsay 👋
 ___
< 👋 >
 ---
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
✨  Done in 0.16s.
```

## Pourquoi ?

### Centraliser et uniformiser

La section `scripts` est utilisée comme point d'entrée commun pour toute l'équipe et les outils d'automatisation afin d'uniformiser les commandes utilisées et de centraliser l'information.

### Utiliser les dépendances locales

En lançant la commande `yarn start`, Yarn va d'abord rechercher la commande `webpack-dev-server` dans le dossier `node_modules` local *(plus exactement, le dossier `node_modules/.bin`)* avant d'utiliser les commandes globalement installées sur la machine.

Le but est d'éviter les dépendances globales et donc l'hétérogénéité des versions.

Les développeurs et outils d'automatisation n'ont donc besoin que de deux prérequis : NodeJS et Yarn.

### Automatiser

Avant et après l'exécution de `yarn install`, Yarn lance respectivement les scripts `preinstall` et `postinstall` qui permettent donc d'enrichir la phase d'installation afin d'installer par exemple les dépendances d'autres langages et frameworks.

Si vous déployez votre application sur Heroku <https://www.heroku.com/>, Heroku détecte la présence du fichier `package.json`, lance la commande `yarn install` puis la commande `yarn heroku-postbuild` qui vous permet de personnaliser le build de votre application.

## yarn run vs npm run

Pour exécuter une commande avec yarn et lui passer des options, il n'y a pas de surprise :

```bash
yarn hello -s # -s => stoned cow
```

```bash
yarn run v1.6.0
$ cowsay 👋 -s
 ___
< 👋 >
 ---
        \   ^__^
         \  (**)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||
✨  Done in 0.16s.
```

Cela exécute donc la commande `cowsay 👋 -s`.

En revanche, avec NPM, il faut lancer la commande `run` et malheureusement c'est NPM qui consomme les options et dans ce cas particulier, NPM interprète l'option `-s` qui dans son cas veut dire `--silent`.

```bash
npm run hello -s
```

```bash
 ___
< 👋 >
 ---
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

Pour passer des options, voici la syntaxe à utiliser :

```bash
npm run hello -- -s
```

```bash
> cowsay 👋 "-s"

 ___
< 👋 >
 ---
        \   ^__^
         \  (**)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||
```

{% hint style="info" %}
Donc pour résumer :

`yarn hello -s` <=> `npm run hello -- -s`

A vous de choisir.
{% endhint %}

{% hint style="danger" %}
Pour remédier à certains de ces problèmes et apporter de nouvelles fonctionnalités, l'équipe NPM a produit une nouvelle commande `npx` que l'on vous recommande d'éviter principalement pour des raisons de sécurité.\
**Le danger de cette commande est qu'elle installe automatiquement le module que vous lui passez en paramètre et l'exécute immédiatement.**

Une typo et vous êtes cuits si vous tombez sur module malveillant.

Exemple :

```bash
npx run hello
```

```bash
Error: Cannot find module './hello'
```

Fiouf ! Nous venons d'installer inconsciemment le module run (<https://yarnpkg.com/en/package/run>) qui a ensuite essayer d'exécuter un fichier hello de notre projet. 😱

> StackOverflow + Social Engineering = Remote Code Execution
> {% endhint %}


# Mise à Jour et Automatisation

## Liste des dépendances "outdated"

```bash
yarn outdated
```

![Yarn Outdated Dependencies](/files/-LAY6OOOifPNwwLs-JFZ)

## Mise à jour des dépendances et sous dépendances

```bash
yarn upgrade
```


# Chrome

Le navigateur Chrome implémente les dernières fonctionnalités JavaScript et dispose des meilleurs outils de développement et de debug.


# IntelliJ / WebStorm / VSCode

## JetBrain's IntelliJ IDEA & WebStorm

[IntelliJ IDEA](https://www.jetbrains.com/idea/) & [WebStorm](https://www.jetbrains.com/webstorm/) sont deux IDEs produits par [JetBrains](https://www.jetbrains.com/) *(connu pour Android Studio, PyCharm, ReSharper...).*

**IntelliJ IDEA** est un IDE Java initialement mais il dispose de plugins pour de nombreux autres langages *(JavaScript, Python, TypeScript etc...).*\
**WebStorm** est simplement une version limitée d'IntelliJ IDEA sans support pour le Java ou le Python par exemple.

Si vous prévoyez de travailler sur plusieurs technologies et langages, il est recommandé d'utiliser IntelliJ IDEA.

IntelliJ IDEA propose trois versions :

* **community** : open source et gratuite mais limitée en fonctionnalités,
* **ultimate** : payante mais complète,
* **EAP (Early Access Program)** : ou la version next et pourtant stable. Elle est proposée avec une période d'essaie de 30 jours prolongée de 30 jours à chaque mise à jour (sachant que les mises à jours sont fréquentes 🎉).

{% hint style="info" %}
**IntelliJ Ultimate** ne coûte pas plus qu'un bon espresso ☕️par jour.

**WebStorm** ne coûte pas plus cher qu'un café soluble par jour.
{% endhint %}

{% hint style="info" %}
Si vous optez pour IntelliJ plutôt que WebStorm, pensez à installer le plugin **Karma** pour pouvoir facilement "debug" les tests unitaires.
{% endhint %}

### JetBrains Toolbox

{% embed url="<https://www.jetbrains.com/toolbox/>" %}

JetBrains Toolbox est un outil très pratique vous permettant d'installer et mettre à jour les outils JetBrains de votre choix. Il permet aussi un accès rapide à vos projets.

![JetBrains Toolbox](/files/-LAXbDRv3QWVWqZzYYBv)

### Points forts

Les IDEs JetBrains :

* fournissent nativement de nombreux plugins pré-installés,
* proposent des recommandations et actions "intelligentes",
* proposent automatiquement des plugins adaptés à votre besoin et parfaitement préconfigurés.



## JetBrains IDE Support Chrome Extension

Extension Chrome pour "debug" vos applications en toute simplicité.

{% embed url="<https://chrome.google.com/webstore/detail/jetbrains-ide-support/hmhgeddbohgjknpmjagkdomcpobmllji>" %}

## Visual Studio Code

{% embed url="<https://code.visualstudio.com/>" %}

Visual Studio Code est un éditeur de code Microsoft gratuit et open-source.

Ses points forts sont son prix et sa légèreté.

Contrairement à IntelliJ, avec Visual Studio Code, c'est à vous de rechercher et configurer les plugins qu'il vous faut.

{% hint style="info" %}
Pensez à mesurer le temps passé à rechercher et configurer les plugins / extensions.
{% endhint %}


# Raccourcis clavier IntelliJ / WebStorm

Voici la liste des **raccourcis les plus utiles** à connaître pour l'utilisation des IDE JetBrains. Vous pourrez également retrouver sur le site de l'éditeur, une liste plus complète de [raccourcis claviers](https://www.jetbrains.com/help/webstorm/mastering-keyboard-shortcuts.html#d1320088e89) *(*[*https://www.jetbrains.com/help/webstorm/mastering-keyboard-shortcuts.html#d1320088e89*](<&#xA;https://www.jetbrains.com/help/webstorm/mastering-keyboard-shortcuts.html#d1320088e89&#xA;>)*)*.&#x20;

## Navigation rapide : Lancer une action / Ouvrir un fichier /  Localiser une classe...

L'éditeur dispose de centaines d'options et d'actions. Pour s'y retrouver rapidement sans épuiser l'ensemble des menus de l'IDE, il existe une entrée spécifique "Find Actions" dans le menu Aide.

Pour le faire apparaître, il suffit d'appuyer deux fois sur la touche Shift et taper le début du nom de l'action ou du fichier recherché

![FindAction](https://www.jetbrains.com/help/img/idea/2018.2/ws_gotoAction.png)

Le système de tabulation permet de rechercher précisément "partout" ou dans un sous-ensemble de l'IDE *(projet, action IDE, ...)*

Lorsque vous recherchez *une action*, le **raccourci clavier actif** apparaît à droite du nom de la commande, vous permettant d'apprendre au fur et à mesure de nouveaux raccourcis.

## Quelques raccourcis utiles

| Action                                  | Raccourcis Windows         | Raccourcis Mac             |
| --------------------------------------- | -------------------------- | -------------------------- |
| Utiliser les tips de l'IDE              | Alt+Enter                  | Alt+Enter                  |
| Naviguer vers un composant              | Ctrl+Click                 | Ctrl+Click                 |
| Edition multi pointeurs                 | Alt+Click Gauche           | Alt+Shift+Click Gauche     |
| Sélectionner occurrence d'un mot clef   | Alt+J                      | Cmd+G                      |
| Re-indentation automatique des lignes   | Ctrl+Alt+L                 | Cmd+G                      |
| Re-indentation alphabétique des imports | Ctrl+Alt+O                 | Ctrl+Alt+O                 |
| Déplacer une ligne de code              | Ctrl+Shift+Fleche haut/Bas | Ctrl+Shift+Fleche haut/Bas |
| Documentation rapide                    | Ctrl+Q                     | Ctrl+J                     |

## Liste des raccourcis claviers Windows/Linux & Mac IntelliJ

<https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf>

{% embed url="<https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf>" %}

## 10 Raccourcis indispensables à connaître

<https://blog.jetbrains.com/webstorm/2015/06/10-webstorm-shortcuts-you-need-to-know/>

{% embed url="<https://blog.jetbrains.com/webstorm/2015/06/10-webstorm-shortcuts-you-need-to-know/>" %}

## Démonstration de WebStorm avec Victor Savkin

<https://www.youtube.com/watch?v=upgjCMHGpwo>

{% embed url="<https://www.youtube.com/watch?v=upgjCMHGpwo>" %}

## Remerciement <a href="#remerciement" id="remerciement"></a>

Merci à Yannick BIET pour sa contribution !

<https://github.com/YannickBiet>

{% embed url="<https://github.com/YannickBiet>" %}

[https://twitter.com/yannickbiet](https://twitter.com/yannickbiet?lang=fr)


# Floobits

{% embed url="<http://floobits.com/>" %}
Floobits
{% endembed %}

Floobits est une plateforme de développement collaboratif proposant un plugin pour les IDEs JetBrains.

{% hint style="info" %}
Visual Studio Code propose un équivalent intéressant : Live Share <https://code.visualstudio.com/visual-studio-live-share> mais il est actuellement en "private preview".&#x20;
{% endhint %}


# Angular CLI

{% embed url="<https://cli.angular.io/>" %}
Angular CLI
{% endembed %}

Angular CLI est un outil permettant de créer, construire, générer et tester vos applications et librairies Angular.

Sans Angular CLI, la création et la construction d'une application Angular nécessite l'utilisation et la maîtrise de nombreux outils : typescript, webpack, karma, protractor, istanbul etc...

> Exemple du package.json d'un boilerplate *(squelette de projet)* avant Angular CLI  <https://github.com/gdi2290/angular-starter/blob/master/package.json>

## Installation

La commande suivante installe le module `@angular/cli`.

{% hint style="info" %}
Les modules Angular officiels sont préfixés par `@angular/`.

Il s'agit d'un "scope" NPM. Cela nous garantit que seuls les administrateurs du groupe "angular" peuvent déployer des modules dans ce "scope" *(avec idéalement deux facteurs d'authentification)*.\
<https://docs.npmjs.com/misc/scope>\
<https://docs.npmjs.com/getting-started/using-two-factor-authentication>
{% endhint %}

```bash
yarn global add @angular/cli
```

L'installation de ce module mettra à votre disposition la commande `ng` qui vous permettra plus tard de créer votre application Angular.

## Documentation

La documentation d'Angular CLI est disponible sous forme de wiki <https://github.com/angular/angular-cli/wiki>

## Schematics

La génération et mise à jour automatique du code fournie par Angular CLI se base sur l'outil Schematics qui permet également de définir nos propres "schematics". Ces "schematics" peuvent être vues comme des "recettes" qui pourront être utilisées en ligne de commande pour générer du code, le corriger ou le mettre à jour afin de respecter les derniers "breaking changes" ou "guidelines" du framework ou d'une librairie.

<https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2>


# StackBlitz

Pour vous amuser ou échanger des exemples qui marchent *(ou plus souvent l'inverse pour démontrer un dysfonctionnement)*, n'hésitez pas à utiliser [Stackblitz](https://stackblitz.com) qui propose un VSCode "in-the-browser" tout en intégrant le build Angular.

<https://stackblitz.com/fork/angular>


# Compodoc

Compodoc est un outil open-source permettant de générer rapidement une documentation de votre application Angular.

## Installation

La commande suivante installe le module `@compodoc/compodoc`.

```bash
yarn add --dev @compodoc/compodoc
```

## Utilisation

Définissez ensuite un npm "script" dans votre fichier `package.json`

```javascript
"scripts": {
  "compodoc": "compodoc -p src/tsconfig.app.json"
}
```

Et lancez la ensuite dans votre terminal :

```bash
yarn compodoc
```

Vous obtiendrez ensuite la documentation générée dans le dossier `documentation`. Ouvrez la dans votre navigateur préféré.

## Example

Un example de documentation générée est disponible sur ce lien : <https://compodoc.github.io/compodoc-demo-todomvc-angular/>

## Remerciement

Merci à Vincent OGLOBLINSKY pour sa contribution !

<https://github.com/vogloblinsky>

{% embed url="<https://github.com/vogloblinsky>" %}

<https://twitter.com/vogloblinsky>


# Angular


# Bootstrap

## Prérequis

Installation des outils suivants :

{% content-ref url="/pages/-LAMjNHvWi7kD6OoNzMu" %}
[Git](/tools/git)
{% endcontent-ref %}

{% content-ref url="/pages/-LAMjNHxOMsk08oRX-6R" %}
[NodeJS](/tools/nodejs)
{% endcontent-ref %}

{% content-ref url="/pages/-LAMjNHysypEoFR5lFVP" %}
[Yarn](/tools/yarn)
{% endcontent-ref %}

{% content-ref url="/pages/-LAQ3Zbow6iB85A4ZTY1" %}
[Angular CLI](/tools/angular-cli)
{% endcontent-ref %}

{% content-ref url="/pages/-LAMjNI-S3isSxZPX3Dc" %}
[IntelliJ / WebStorm / VSCode](/tools/intellij-webstorm-vscode)
{% endcontent-ref %}

{% content-ref url="/pages/-LAMjNHzsO8taWedFX0Y" %}
[Chrome](/tools/chrome)
{% endcontent-ref %}

## Création de l'application Angular

Pour créer une application Angular, nous utiliserons la commande Angular CLI : `ng`.

Il faut d'abord indiquer à Angular CLI que nous utiliserons Yarn plutôt que NPM.

```bash
ng config cli.packageManager yarn --global
```

{% hint style="danger" %}
En cas de problème avec cette commande, vous pouvez ignorer cette étape puis ajouter l'option `--skip-install` afin d'éviter l'installation avec npm puis installer les dépendances manuellement avec `yarn`.\
`ng new book-shop --prefix wt --skip-install && cd book-shop && yarn install`
{% endhint %}

La commande ci-dessous va créer un projet Angular *(avec une application)* dans le dossier `book-shop`.

```bash
ng new book-shop --prefix wt
```

{% hint style="info" %}
L'option --prefix permet d'indiquer le préfixe que nous utiliserons pour nos composants Angular (entre autres) afin de les reconnaître rapidement dans le code HTML (e.g.: \<wt-book-list>).

Autrement, la valeur par défaut utilisée par Angular CLI est app  et notre composant se nommerait alors \<app-book-list>. Ici, l'acronyme wt fait référence à Wishtack.
{% endhint %}

La commande `ng new` :

1. Crée le squelette du projet dans le dossier `book-shop` *(`package.json`, `.gitignore`, code source, configurations etc...)*
2. Installe implicitement à l'aide de la commande `yarn install` toutes les dépendances indiquées par défaut dans le fichier `package.json` *(sauf si l'on ajoute l'option `--skip-install`)*.&#x20;
3. Initialise un "repository" git et ajoute le fichier `yarn.lock` généré par l'étape précédente.

{% hint style="info" %}
Vous obtenez donc une application opérationnelle dont tous les fichiers ont été "commit" proprement.\
Il vous suffit alors d'ajouter un "remote" pour partager votre code source avec les copains.\
<https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes>\
<https://help.github.com/articles/adding-a-remote/>
{% endhint %}

{% hint style="info" %}
Le module `@angular/cli` installé globalement n'est utile que pour la création de l'application.\
Pour la suite, le module `@angular/cli` sera installé localement dans chaque projet.

Une fois le projet initialisé, les développeurs n'ont plus besoin d'installer le module `@angular/cli` globalement.
{% endhint %}

## Développement

Il suffit maintenant d'ouvrir le projet sur votre IDE puis de lancer la commande suivante :

```bash
yarn start
```

... ou ajouter l'option `--open` pour ouvrir une fenêtre de votre navigateur sur l'application : <http://localhost:4200>

```bash
yarn start --open
```

Cette commande est un "alias" pour `ng serve` qui lui-même lance un serveur de développement à l'aide de `webpack-dev-server` qui s'occupe principalement des tâches suivantes :

* **Watch et build avec webpack** : à chaque fois que vous modifiez le code source de votre application, webpack va détecter les changements et "rebuild" l'application.
* **Livereload** : à chaque "rebuild", webpack-dev-server communique avec vos fenêtres de navigation ouvertes sur l'application (par websocket) pour rafraîchir le contenu. C'est ainsi qu'à chaque changement, vous pouvez en observer le résultat quasi-immédiatement sur vos navigateurs.

&#x20;Vous pouvez également lancer l'application *(ou n'importe quel script yarn)* directement depuis l'IDE.

![](/files/-LAYWpASX1hYOFKFaURi)

Et c'est parti :

![](/files/-LAYWF32dCFt2j16EOH2)

## Mise à jour

La commande update d'Angular CLI permet :

* de mettre à jour les dépendances de votre projet,
* d'adapter sa structure aux nouvelles version d'Angular CLI *(e.g. : renommage et restructuration du fichier `.angular-cli.json` => `angular.json`)*.
* à partir d'Angular 6, d'adapter votre code quand la mise à jour d'une dépendance le nécessite *(e.g. : adaptations en fonction des mises à jour d'Angular Material)*.&#x20;

```bash
yarn ng update
```

{% hint style="warning" %}
Nous utilisons la commande `yarn ng update` au lieu de `ng update` afin d'utiliser la version Angular CLI locale à votre projet plutôt que la version installée globalement sur votre environnement *(si Angular CLI est installé globalement)*.&#x20;
{% endhint %}

Cf. <https://update.angular.io>


# Composants

## Le concept

L'un des principaux concepts d'Angular est de voir une application comme une arborescence de composants.

> A Component controls a patch of screen real estate that we could call a view, and declares reusable UI building blocks for an application.

### Custom Elements

C'est d'ailleurs un concept que l'on retrouve dans tous les frameworks modernes et même nativement avec les Custom Elements : <https://developers.google.com/web/fundamentals/web-components/customelements>

Exemple à essayer dans la console de votre navigateur :

```javascript
/* The custom element definition. */
class HelloElement extends HTMLElement {
    connectedCallback() {
        this.textContent = 'Hello 🌍';
    }
}

/* Registering the custom element. */
customElements.define('wt-hello', HelloElement);

/* Injecting the element using innerHTML... */
document.body.innerHTML = '<wt-hello></wt-hello>';

/* ... or using appendChild & createElement. */
document.body.appendChild(document.createElement('wt-hello'));
```

### Custom Elements & Web Components Tooling

<https://stenciljs.com/>

{% embed url="<https://stenciljs.com/>" %}

<https://github.com/Polymer/lit-element>

{% embed url="<https://github.com/Polymer/lit-element>" %}

## Separation of Concerns

Les composants permettent une meilleure décomposition de l'application, facilitent le refactoring et le testing.

## Isolement

Chaque composant est isolé des autres composants. Il n'hérite pas implicitement des attributs des composants parents.


# Root Component

Une application Angular est généralement composé d'un “root component“ qui sera l'élément le plus haut de la hiérarchie de composants Angular.

## `index.html`

Ce composant est utilisé dans le fichier `src/index.html` qui est la page HTML accueillant l'application Angular.

{% tabs %}
{% tab title="src/index.html" %}

```markup
<!doctype html>
<html lang="en">
<head>
    ....
</head>
<body>
    <wt-root></wt-root>
</body>
</html>
```

{% endtab %}
{% endtabs %}

Il s'agit du tag HTML `<wt-root>` utilisant le préfixe `wt` choisi lors de la création de l'application.

## `AppComponent`

Le composant est défini dans le fichier `src/app/app.component.ts` dont voici une version plus minimaliste :

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
import { Component } from '@angular/core';

@Component({
    selector: 'wt-root',
    template: `
<h1>Hello real 🌎!</div>
`
})
export class AppComponent {
}
```

{% endtab %}
{% endtabs %}

**Un composant Angular n'est rien d'autre qu'une classe...**

... avec un décorateur `@Component`.

## `@Component`

Ce décorateur prend en paramètre une configuration qui contient au minimum :

* **selector :** le sélecteur CSS qui permettra de lier le tag HTML de l'élément et le code du composant.
* **template :** le template HTML utilisé par Angular pour générer le contenu de l'élément dans le DOM.

Par bonne pratique, nous n'utiliserons pas la propriété `template` pour définir le template HTML mais plutôt **`templateUrl`** qui permet d'indiquer le chemin vers le fichier HTML du template associé au composant.

```typescript
templateUrl: './app.component.html'
```

Le résultat sera identique *(le fichier HTML n'est pas téléchargé en runtime mais plutôt consommé au "build")* mais le code sera plus clair et la "separation of concerns" mieux respectée.

{% hint style="success" %}
La convention de nommage pour un composant est la suivante :

* Les fichiers sont en "kebab-case" suffixés par `.component.ext`. *(e.g. :* `book-preview.component.ts` & `book-preview.component.html`*)*
* La classe est en "PascalCase" suffixée par `Component`. *(e.g. :* `BookPreviewComponent`*)*
* Le sélecteur CSS doit être un sélecteur de tag en "kebab-case" préfixé par le suffixe du produit. *(e.g. :* `wt-book-preview`*).*
  {% endhint %}

{% hint style="warning" %}
Le squelette par défaut ne respecte pas la dernière règle *(utilisation de `wt-root` au lieu de `wt-app`)*.

N'hésitez pas à "refactor".
{% endhint %}

![Selector refacotoring](/files/-LAn3CtqTeOMaetwq_XS)

## Code source

<https://github.com/wishtack/wishtack-book-shop/tree/1-bootstrap>

## Démo StackBlitz

{% embed url="<https://stackblitz.com/github/wishtack/wishtack-book-shop/tree/1-bootstrap>" %}


# Template Interpolation

Comme de nombreux langages de "templating", Angular utilise la syntaxe "double curly braces" pour l'interpolation :

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
@Component({
    selector: 'wt-app',
    templateUrl: './app.component.html'
})
export class AppComponent {
    bookName = 'eXtreme Programming Explained';
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<div>
    <span>{{ bookName }}</span>
</div>
```

{% endtab %}
{% endtabs %}

La syntaxe d'interpolation permet d'accéder directement aux propriétés du composant associé *(un peu comme si toutes les expressions étaient préfixées par un `this.`).* En effet, l'instance de la classe `AppComponent` est le contexte utilisé par le moteur de rendu afin d'évaluer les expressions.

{% hint style="success" %}
Les expressions utilisées dans le "template interpolation" (et les "[property binding](/angular/composants/property-binding)") doivent rester **simples**.\
Vous pouvez utiliser des méthodes de votre composant mais elles doivent être **performantes**, **sans effet de bord** et produire des résultats **prédictibles**.
{% endhint %}

{% hint style="danger" %}
L'interpolation ne doit être utilisée que pour définir le contenu d'un élément HTML.

N'utilisez donc jamais la syntaxe d'interpolation pour contrôler les attributs d'un élément par exemple !

😱`<img src="{{ pictureUrl }}">`😱

Préférez l'utilisation du [Property Binding](/angular/composants/property-binding) pour les raisons suivantes :

* Les attributs d'un élément ne sont pas tous associés à des propriétés et vice versa.
* Les attributs d'un élément ne sont pas forcéments toujours synchronisés avec ses propriétés.
* Les attributs d'un élément ne sont pas toujours du même type que la propriété associée *(i.e. :* *`element.getAttribute('disabled') !== element.disabled`).*
* Certaines propriétés attendent des valeurs complexes alors que les attributs ne permettent de passer que des valeurs de type "string".
  {% endhint %}


# Property Binding

L'interpolation ne suffira pas pour tout contrôler *(images, styles, etc...)*.

Mieux que le contrôle des attributs, le "property binding" nous permet de contrôler n'importe quelle propriété d'un élément du DOM en s'inspirant de la syntaxe native *(Vanilla JS)* : `button.disabled = false` ou encore `button['disabled'] = false` *(pour désactiver un bouton par exemple)*.

Ce qui donne la syntaxe suivante :

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<img [alt]="bookName" [src]="bookPictureUrl">


<button type="button" [disabled]="!isAvailable">BUY</button>
```

{% endtab %}
{% endtabs %}

Ou en laissant le code respirer :

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<img
        [alt]="bookName"
        [src]="bookPictureUrl">

<button
        type="button"
        [disabled]="!isAvailable">BUY</button>
```

{% endtab %}
{% endtabs %}

Les données proviennent encore du code TypeScript du composant.

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
...

export class AppComponent {

    bookName = 'eXtreme Programming Explained';
    bookPictureUrl = 'https://robohash.org/xp?set=set4';
    isAvailable = false;

}
```

{% endtab %}
{% endtabs %}

![Exemple de "property binding" Angular](/files/-LAnJKh41Im-AN0Kz9u_)


# Class & Style Binding

## Class Binding

```markup
<div [class.wt-important]="isImportant">Important Stuff</div>
```

{% hint style="warning" %}
Evitez la construction manuelle de la "string" class :\
`classString += ' b'` puis `<div [class]="classString"></div>`.
{% endhint %}

<https://angular.io/guide/template-syntax#class-binding>

{% embed url="<https://angular.io/guide/template-syntax#class-binding>" %}

## Style Binding

```markup
<button [style.background-color]="isValid ? null : 'red'">Save</button>
```

```markup
<button [style.font-size.em]="isImportant ? 2 : 1" >Big</button>
```

<https://angular.io/guide/template-syntax#style-binding>

{% embed url="<https://angular.io/guide/template-syntax#style-binding>" %}


# Event Binding

Grâce à l'interpolation et le "property binding", nous contrôlons le contenu affiché mais cela manque d'interactions avec l'utilisateur.

Il nous faut ajouter des "listeners" d'évènements déclenchés sur les éléments du DOM afin de modifier l'état de notre application et laisser Angular mettre à jour la "view".

La syntaxe Angular ci-dessous est équivalente au code natif `button.addEventListener('click', () => this.buy())`.

{% tabs %}
{% tab title="src/app.component.html" %}

```typescript
<button
        type="button"
        (click)="buy()">BUY</button>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
...
export class AppComponent {

    buy() {
        ...
    }

}
```

{% endtab %}
{% endtabs %}

## `$event`

La valeur de l'événement peut être récupérer via la variable `$event`.

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<div (drop)="onDrop($event)">BUY</div>
```

{% endtab %}
{% endtabs %}


# \*ngIf

Alors que le [template interpolation](/angular/composants/template-interpolation) et le [property binding](/angular/composants/property-binding) permettent de modifier l'affichage et le contenu, ils ne permettent pas de modifier la structure du DOM en ajoutant ou en retirant des éléments par exemple.

Pour remédier à cette limitation, Angular fournit des **directives structurelles** qui permettent de modifier la structure du DOM.

L'une de ces directives les plus utilisées est le `ngIf`.

Si l'expression associée à la directive est "falsy" alors l'élément et son contenu sont retirés du DOM *(ou jamais ajoutés).*

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<button *ngIf="isAvailable">BUY</button>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
...
export class AppComponent {
    isAvailable = false;
}
```

{% endtab %}
{% endtabs %}

## \*ngIf vs "safe navigation operator"

Les expressions utilisées à l'intérieur ne sont donc jamais évaluées et dans certains cas cela évite certaines erreurs.

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<div>
    <span>{{ book.name }}</span>
</div>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
...
export class AppComponent {
    book = null;
}
```

{% endtab %}
{% endtabs %}

```
TypeError: Cannot read property 'name' of undefined
```

Il suffit alors d'utiliser `ngIf` pour retirer tout le bloc *(si cela est nécessaire)*.

{% hint style="warning" %}
Si le cas ne doit jamais se produire alors il vaut mieux déclencher une erreur bien bruyante plutôt que de camoufler le problème.
{% endhint %}

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<div *ngIf="book">
    <span>{{ book.name }}</span>
</div>
```

{% endtab %}
{% endtabs %}

Angular propose également une opérateur de "safe navigation" qu'il vaut mieux éviter car les éléments sont présents dans le DOM mais vides et cela peut avoir des impacts sur l'affichage et le styling.

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<div>
    <span>{{ book?.name }}</span>
</div>
```

{% endtab %}
{% endtabs %}

## `switch / case` & `if / then / else`

Angular propose des directives pour implémenter des "`switch / case`" et des `"if / then / else`" dans les templates.

<https://angular.io/api/common/NgIf>\
<https://angular.io/api/common/NgSwitch>

{% hint style="danger" %}
Nous vous recommandons d'éviter leur utilisation pour des raisons de lisibilité et de maintenabilité.

Pour répondre à ce genre de besoins, Angular propose des solutions bien plus élégantes avec l'injection dynamique de composants par exemple *(sujet abordé plus tard dans ce guide)*.

Chez Wishtack, nous considérons l'usage de "`if / else`" et de "`switch / case`" comme des codes smells peu importe le langage. Leur utilisation dans des templates est un code smell encore plus fort.
{% endhint %}

## Prochains workshops : 10% de réduction avec le code GUIDEANGULAR

{% embed url="<https://www.eventbrite.com/e/angular-unit-testing-workshop-fondamentaux-tdd-francais-tickets-132818877839>" %}
Angular Unit-Testing Workshop - Fondamentaux & TDD
{% endembed %}


# \*ngFor

La directive structurelle `ngFor` permet de boucler sur un array et d'injecter les éléments dans le DOM.

```markup
<ul>
    <li *ngFor="let book of bookList">{{ book.name }}</li>
</ul>
```

{% tabs %}
{% tab title="src/app.component.ts" %}

```typescript
export class AppComponent {
    bookList = [
        {
            name: 'eXtreme Programming Explained'
        },
        {
            name: 'Clean Code'
        }
    ];
}
```

{% endtab %}
{% endtabs %}

Il est possible de récupérer d'autre informations telles que l'index de l'élément :

* `index` : position de l'élément.
* `odd` : indique si l'élément est à une position impaire.
* `even` : indique si l'élément est à une position paire.
* `first` : indique si l'élément est à la première position.
* `last` : indique si l'élément est à la dernière position.

{% tabs %}
{% tab title="src/app.component.html" %}

```markup
<ul>
    <li *ngFor="let book of bookList; let index = index; let isFirst = first; let isOdd = odd;">
        <span>{{ index }}</span>
        <span>:</span>
        <span>{{ book.name }}</span>
        <span>( isFirst: {{ isFirst }}, isOdd: {{ isOdd }} )</span>
    </li>
</ul>
```

{% endtab %}
{% endtabs %}

![Exemple ngFor](/files/-LAr6-uOIhMODr_FYO7h)

## Prochains workshops : 10% de réduction avec le code GUIDEANGULAR

{% embed url="<https://www.eventbrite.com/e/angular-unit-testing-workshop-fondamentaux-tdd-francais-tickets-132818877839>" %}
Angular Unit-Testing Workshop - Fondamentaux & TDD
{% endembed %}

## Pour plus d'actus Angular, JavaScript et Web, suivez-moi sur Twitter !

{% embed url="<https://twitter.com/yjaaidi>" %}


# L'approche MVC

## MVC (Model-View-Controller)

Angular repose principalement sur une approche MVC où :

* **Le "controller" et le "model"** sont représentés par l'instance de la classe TypeScript du composant.
* **La "view"** est le DOM (ou autre) généré à partir du template.
* La "view" est générée à partir des instructions du "template".
* La "view" déclenche des actions sur le "controller" via des "outputs" (ou [event binding](/angular/composants/event-binding)).
* Le "controller" met à jour l'état du "model".
* Grâce à son mécanisme de "Change Detection", Angular détecte les changements et met à jour la "view" si nécessaire.

![Angular MVC](/files/-LArFfYuZAd9AXZ_AN8Q)

## Violations du MVC Angular

Malheureusement, il arrive souvent *(et dans de nombreux exemples* [*https://angular.io/guide/user-input#get-user-input-from-a-template-reference-variable*](https://angular.io/guide/user-input#get-user-input-from-a-template-reference-variable)*)* que l'on cède à une approche impérative traditionnelle où le "controller" se permet d'accéder explicitement à la "view" pour lire ou modifier son contenu.

{% hint style="danger" %}
Evitez d'accéder directement à votre "view" depuis le "controller" via des "template local variables" ou le décorateur `@ViewChild` !

Avec cette approche impérative, on ne respecte plus le cycle MVC, on crée alors une dépendance entre le "controller" et la "view" que nous avions éviter jusqu'à présent et le "model" n'est plus la "single source of truth".
{% endhint %}

Cela est bien sûr inévitable dans des cas très particuliers, localisés et bas niveau *(e.g. : implémentation d'un wrapper Angular pour une librairie JavaScript)*.

![Angular MVC violations](/files/-LArFlGSj7A9Aywy3R72)

## MVVM (Model-View-ViewModel)

L'approche MVVM est possible dans certains cas étant donné qu'il ne s'agit de rien d'autre qu'une approche MVC dont le Controller est implicite mais il vaut mieux l'éviter pour garder le contrôle de l'état de votre modèle.


# Création de Composants

Pour créer de nouveaux composants, il suffit de créer la classe du composant, le fichier de template associé et surtout ajouter la classe à la liste des `declarations` du module associé.\
Pour le moment, nous n'avons qu'un seul module `AppModule` dont on analysera le contenu plus tard dans la section [Project Structure & Modules](/angular/project-structure-and-modules).

La création manuelle de ces fichiers peut s'avérer fastidieuse au quotidien et surtout "error-prone". C'est pour cette raison qu'Angular CLI fournit nativement des commandes permettant de générer le code nécessaire à la déclaration d'un composant *(entre autres)*. Ces générateurs de code se basent sur l'outil [Schematics](/tools/angular-cli#schematics) fourni par Angular.

## Génération du composant `book-preview`

Pour générer un composant, il suffit de lancer la commande suivante :

```bash
yarn ng generate component book/book-preview
```

Nous avons décidé d'indiquer le "path" cible `book/` afin de regrouper tous les composants *(et autres fichiers)* associés à la fonctionnalité "book" dans ce dossier là. Ce dossier deviendra plus tard un "[feature module](/angular/project-structure-and-modules)".

{% hint style="info" %}
Pour un projet contenant une seule application, le "root path" est `src/app`.
{% endhint %}

![Génération d'un composant avec Angular CLI](/files/-LAwMJe6FzWHRpPdlkqo)

Cela génère le contenu suivant en respectant le "[style guide](https://angular.io/guide/styleguide)" Angular :

* `book-preview.component.ts` : Classe TypeScript `BookPreviewComponent` du composant .
* `book-preview.component.html` : Template HTML du composant .
* `book-preview.component.scss` : Fichier CSS du composant. Dans notre cas, le fichier a une extension `.scss` car nous avons opté pour le [SASS](https://sass-lang.com/) en l'indiquant dans notre configuration `yarn ng set defaults.styleExt=scss`.
* `book-preview.component.spec.ts` : Squelette des tests unitaires du composant .

... et met à jour le fichier `app.module.ts` dont nous analyserons le contenu [plus tard](/angular/composants/creation-de-composants).

{% hint style="info" %}
N'oubliez pas d'ajouter et de "commit" les fichiers générés et modifiés.
{% endhint %}

Nous pouvons désormais utilisé notre nouveau composant dans le template du composant `app` par exemple :

![Insertion d'un composant](/files/-LAwQsHwvEXPiiaFQpJd)

{% tabs %}
{% tab title="src/app/app.component.html" %}

```markup
<wt-book-preview></wt-book-preview>
<wt-book-preview></wt-book-preview>
```

{% endtab %}
{% endtabs %}

![Contenu par défaut du composant](/files/-LAwR68ZzXNZfJuOg-Nw)

## `yarn ng` vs. `ng`

{% hint style="success" %}
Il est recommandé d'utiliser la commande `yarn ng` au lieu de `ng` *(contrairement à ce que l'on peut remarquer sur de nombreuses documentations et exemples)* pour les raisons suivantes :

* La commande `yarn ng`, utilise la version d'Angular CLI installée localement sur votre projet. Tous les développeurs *(et outils)* utilisent alors la même version.
* La commande `ng` n'est pas forcément disponible globalement dans tous les environnements. Certains développeurs *(ou outils)* risquent donc d'être surpris en exécutant la même commande sur leur environnement.

Heureusement, si les versions de l'Angular CLI globale et la locale ne correspondent pas, Angular CLI lance la version locale en affichant une alerte.
{% endhint %}

{% hint style="success" %}
Pour éviter tous ces ennuis et pour pouvoir factoriser dans votre équipe les options utilisées pour générer des composants, pourquoi ne pas ajouter un script à votre `package.json`:

```javascript
"generate:component": "ng generate component --export"
```

permettant aux développeurs de générer des composants avec la commande :

```bash
yarn generate:component book/book-preview
```

... au lieu de devoir expliquer la commande `ng g c` à chaque nouvel arrivant.
{% endhint %}


# Exemple

## Code source

<https://github.com/wishtack/wishtack-book-shop/tree/2-templating>

## Démo StackBlitz

{% embed url="<https://stackblitz.com/github/wishtack/wishtack-book-shop/tree/2-templating>" %}

![Exemple de templating](/files/-LAnUsjBUh5NAeLtCxIJ)


# Container vs. Presentational Components

Supposons que nous disposons dans notre composant `app`, d'une liste `bookList` contenant une liste d'instance d'une classe `Book` que nous souhaitons afficher.

{% tabs %}
{% tab title="book/book.ts" %}

```typescript
export class Book {

    title?: string;

    constructor(args: Book = {}) {
        this.title = args.title;
    }

}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="app.component.ts" %}

```typescript
import { Book } from './book/book';

...
export class AppComponent {
    bookList = [
        new Book({
            title: 'eXtreme Programming Explained'
        }),
        new Book({
            title: 'ReWork'
        })
    ];
};
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Laissez bien sûr l'IDE s'occuper des imports via l'auto-complete ou l'auto-import *(Alt + Enter chez JetBrains)*.
{% endhint %}

![IntelliJ Class Completion](/files/-LAxjo1na3vnXV8LmPkZ)

![IntelliJ Auto Import](/files/-LAxiuLg3ZGiqWdvCRxL)

Il serait intéressant de déléguer l'affichage de chaque `book` au composant `book-preview` que nous pourrons réutiliser plus tard dans d'autres contextes.

Dans ce cas, nous séparons les responsabilités entre le composant `app` et le composant `book-preview`.

{% tabs %}
{% tab title="app.component.html" %}

```markup
<!-- Display one book-preview component for each book... -->
<!-- ...but we need to find some way to pass the book to each component. -->
<wt-book-preview
        *ngFor="let book of bookList"></wt-book-preview>
```

{% endtab %}
{% endtabs %}

## Container Component *(ou Smart Component)*

Le composant `app` **s'occupe donc de la "business logic"** et sélectionne les objets `book` à afficher via la propriété `bookList`.\
Il est donc un "Container Component" qui délègue l'affichage à des "Presentational Components".

## Presentational Component *(ou Dumb Component)*

Le composant `book-preview` ne sait pas d'où provient le `book` à afficher mais il sait l'afficher.\
Il est donc un "Presentational Component" qui est débarrassé de la "business logic".

## Article plus détaillé sur le sujet

{% embed url="<https://blog.wishtack.com/2017/05/05/the-guide-to-building-quality-angular-2-components/>" %}

## Interaction entre Composants

Il ne reste plus au composant app qu'à communiquer chaque `book` au composant `book-preview` associé. Cf. [Interaction entre Composants](/angular/interaction-entre-composants).


# Interaction entre Composants


# Input

## 1. Property Binding

Pour transmettre des données à un "child component", nous allons communiquer avec ce dernier de la même façon que nous contrôlons les propriétés d'un élément natif, c'est à dire à l'aide du [Property Binding](/angular/composants/property-binding) :

```markup
<wt-book-preview [book]="bookList[0]"></wt-book-preview>
```

On obtient alors un "set" implicite de la propriété `book` de l'instance du composant `BookPreviewComponent.`

{% tabs %}
{% tab title="Sous le capot" %}

```typescript
bookPreviewComponent.book = this.bookList[0];
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Remarquez la similarité avec le [Property Binding](/angular/composants/property-binding) sur des éléments natifs.

```markup
<button [disabled]="!isEnabled">
```

```typescript
button.disabled = !this.isEnabled;
```

{% endhint %}

## 2. Déclaration de la propriété

Avec le code précédent, nous obtenons l'erreur suivante :

```
Template parse errors:
Can't bind to 'book' since it isn't a known property of 'wt-book-preview'.
1. If 'wt-book-preview' is an Angular component and it has 'book' input, then verify that it is part of this module.
2. If 'wt-book-preview' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.
```

En effet, le composant book-preview n'a pas de propriété `book`. Il faut donc la déclarer :

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
...
export class BookPreviewComponent {
    book: Book;
}
```

{% endtab %}
{% endtabs %}

... mais heureusement, cela ne suffit pas et nous obtenons toujours la même erreur.

## 3. Décorateur `@Input()`

Par défaut, aucune propriété de composant ne peut être modifiée par [Property Binding](/angular/composants/property-binding). Il faut donc définir les propriétés pouvant servir d' "input" au composant en ajoutant simplement le décorateur `@Input()`.

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
import { Input } from '@angular/core';

...
export class BookPreviewComponent {
    @Input() book: Book;
}
```

{% endtab %}
{% endtabs %}

Voyez ce décorateur comme un contrôle vous permettant de définir la visibilité d'une propriété d'un composant.

{% hint style="danger" %}
N'oubliez pas les parenthèses !

En réalité, `Input` est une "factory" qui retourne un décorateur. Si vous l'utilisez comme décorateur `@Input book`, elle n'aura aucune action et ne fonctionnera donc pas.
{% endhint %}

## Résultat

{% tabs %}
{% tab title="app.component.ts" %}

```typescript
...
export AppComponent {
    bookList = [
        new Book({
            title: 'eXtreme Programming Explained'
        }),
        new Book({
            title: 'ReWork'
        })
    ];
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="app.component.html" %}

```markup
<wt-book-preview
        *ngFor="let book of bookList"
        [book]="book"></wt-book-preview>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
...
export class BookPreviewComponent {
    @Input() book: Book;
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="book-preview\.component.html" %}

```markup
<div>{{ book.title }}</div>
```

{% endtab %}
{% endtabs %}

![](/files/-LAxIXYCtgCF3G9wxGtg)


# Output

## 1. Event Binding

De la même façon que les [Inputs](/angular/interaction-entre-composants/input) permettent de communiquer des données à un "child component", ce dernier peut transmettre des données au "parent component" via un mécanisme d'"Output" similaire à l'[Event Binding](/angular/composants/event-binding) utilisé précédemment pour capturer des événements natifs.

```markup
<wt-book-preview (rate)="onRate($event)"></wt-book-preview>
```

Nous avons inscrits l'expression `onRate($event)` comme "listener" de l'événement `rate`.

{% tabs %}
{% tab title="Sous le capot" %}

```typescript
bookPreviewComponent.rate
    .subscribe((rating) => {
        this.onRate(rating);
    });
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Remarquez la similarité avec l'[Event Binding](/angular/composants/event-binding) sur des événements DOM.

```markup
<button (click)="buy()">BUY</button>
<div (drop)="onDrop($event)"></div>
```

```typescript
button.addEventListener('click', () => this.buy());
div.addEventListener('drop', (dropEvent) => this.onDrop(dropEvent));
```

{% endhint %}

{% hint style="warning" %}
Contrairement aux [Inputs](/angular/interaction-entre-composants/input), si l'"output" n'est pas déclaré correctement, Angular ne produira aucune erreur. Dans ce cas, Angular inscrit notre "listener" à un événement DOM qui n'existe pas et qui ne se produit donc jamais ; notre "listener" ne sera alors simplement jamais appelé.
{% endhint %}

## 2. Déclaration de la propriété et décorateur `@Output()`

En déclarant simplement la propriété `rate` sur le composant `book-preview` :

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
...
export class BookPreviewComponent {
    rate;
}
```

{% endtab %}
{% endtabs %}

... il ne se passe rien mais par analogie avec les [Inputs](/angular/interaction-entre-composants/input), il faut ajouter le décorateur `@Output()` :

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
import { Output } from '@angular/core';

...
export class BookPreviewComponent {
    @Output() rate;
}
```

{% endtab %}
{% endtabs %}

... et nous remarquons alors une erreur très intéressante :

```
TypeError: Cannot read property 'subscribe' of undefined
```

Comme indiqué précédemment, avec l'[Event Binding](/angular/composants/event-binding), si Angular trouve un "output" du même nom, il ajoute un listener dessus avec la méthode `subscribe`. On remarque donc qu'Angular s'attend à ce que les "Outputs" soient des objets qui définissent la méthode `subscribe` *(ou alors quelque chose de similaire à des instances d'"Observables". Cf. RxJS.)*.

## 3. Initialisation de la propriété avec `EventEmitter`

Il faut donc initialiser la propriété. Nous pourrions initialiser la propriété avec n'importe quel `Observable` mais dans la pratique nous utiliserons la classe Angular `EventEmitter` *(qui hérite de la classe Subject d'RxJS qui elle même hérite de la classe `Observable` d'RxJS)*.

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
export class BookPreviewComponent {
    @Output() rate = new EventEmitter();
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
`EventEmitter` est une classe générique et il est recommandé de la typer pour éviter d'émettre des valeurs du mauvais type par erreur ou plus simplement pour indiquer à l'utilisateur du composant le type de données émises par l'"Output" dès la première lecture.

```typescript
@Output() rate = new EventEmitter<number>();
```

{% endhint %}

{% hint style="danger" %}
Faites attention à bien importer la bonne classe `EventEmitter`.
{% endhint %}

![Attention à bien importer le bon \`EventEmitter\`](/files/-LBM-N14JCutx2Bf6U-0)

## 4. Emission de valeurs

Comme son nom l'indique, un `EventEmitter` permet d'émettre des valeurs. Il peut donc être utilisé n'importe où dans la classe `BookPreviewComponent` pour remonter des valeurs au composant parent via la méthode `emit`.

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
export class BookPreviewComponent {

    @Output() rate = new EventEmitter<number>();

    iLoveIt() {
        this.rate.emit(5);
    }

}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="book-preview\.component.html" %}

```markup
<button (click)="iLoveIt()">I LOVE IT</button>
```

{% endtab %}
{% endtabs %}

![](/files/-LAxnw5Wn1pqFyDVtMnj)


# Exemple

## Code source

<https://github.com/wishtack/wishtack-book-shop/tree/3-components-interaction>

## Démo StackBlitz

{% embed url="<https://stackblitz.com/github/wishtack/wishtack-book-shop/tree/3-components-interaction>" %}

![Exemple d'interaction de composants](/files/-LBM-c36GPyQmnSV2NJs)


# Change Detection

Peu importe la librairie ou le framework utilisés, il est nécessaire de synchroniser le modèle avec la vue.

![](/files/-LBXgHnwawY55VP8FlfI)


# Les Approches Possibles

Pour synchroniser le modèle avec la vue, les approches les plus communes sont les suivantes :

## **Approche Impérative Explicite**

Les développeurs mettent à jour explicitement la vue. *(e.g. : Vanilla JS / JQuery / ...)*

```typescript
user.isDisabled = true;
document.querySelector('button').disabled = user.isDisabled;
```

| **Avantages**                                        | **Inconvénients**                                              |
| ---------------------------------------------------- | -------------------------------------------------------------- |
| Simple et performant pour des cas **très basiques**. | Couplage fort avec la vue.                                     |
| Pas besoin de framework.                             | Complexité.                                                    |
|                                                      | Risques importants de désynchronisation du modèle avec la vue. |
|                                                      | Problèmes de performances.                                     |
|                                                      | Bugs fréquents.                                                |

## Approche Impérative Implicite

Les développeurs couplent la vue avec le modèle afin de mettre à jour cette dernière à chaque changement.

```typescript
export class Customer {
    
    set firstName(value) {
        this._firstName = value;
        this.notifyFirstNameChange(value);
    }
    
}

customer.subscribeToFirstNameChange(firstName => nameElement.textContent = firstName);
```

| **Avantages**                           | **Inconvénients**                     |
| --------------------------------------- | ------------------------------------- |
| Synchronisation du modèle et de la vue. | Complexité et comportement implicite. |
|                                         | Problèmes de performances garantis.   |
|                                         | Risques de boucles infinies.          |

## Approche Déclarative

Dans ce cas, ni le controller ni le modèle n'ont conscience de la vue.

En revanche, la vue sait quelles propriétés du modèle surveiller pour maintenir la vue à jour.

### Code du Template

```markup
<div>{{ user.firstName }}</div>
```

### Code Implicite

```typescript
refresh() {

    /* No change. */
    if (lastUserFirstName === user.firstName) {
        return;
    }
    
    /* Update view. */
    divElement.textContent = lastUserFirstName = user.firstName;
    
}
```

| **Avantages**                | **Inconvénients**                           |
| ---------------------------- | ------------------------------------------- |
| Simplicité.                  | Gourmand en performance sans optimisations. |
| Couplage faible avec la vue. |                                             |
| Contrôle total.              |                                             |

### Mais comment ?

... mais **combien y a-t-il de fonctions refresh**, **qui les appelle** et **quand** ?


# Fonctionnement de la Change Detection

Angular adopte une [Approche Déclarative](/angular/change-detection/les-approches-possibles) afin de maintenir en synchronisation le modèle et la vue.

## Change Detector

**Chaque composant dispose d'un "change detector"** qui comme son nom l'indique se charge de détecter les changements de modèle concernant la vue et de mettre à jour la vue en conséquence.

Le "Change Detector" est fortement couplé à la vue du composant associé. Cela lui permet d'**analyser la liste des expressions utilisées dans la vue** *(i.e.* [*Template Interpolation*](/angular/composants/template-interpolation) *ou* [*Property Binding*](/angular/composants/property-binding)*)* *en les comparant à la dernière valeur retournée par chacune d'elles*.

**Si la valeur** retournée par l'expression **change**, **la vue est mise à jour** en conséquence.

## Déclenchement de la "Change Detection"

Pour détecter les changements, Angular utilise la librairie [Zone.js](https://github.com/angular/zone.js) dont le rôle est d'encapsuler et d'intercepter tous les appels asynchrones *(e.g.* `setTimeout`*, event listeners etc...)*.

Avant le chargement d'Angular, Zone.js procède au [Monkey patching ](https://en.wikipedia.org/wiki/Monkey_patch)des fonctions natives permettant d'inscrire des "callbacks" associées à des traitements asynchrones *(e.g. `setTimeout`)* afin de pouvoir détecter chaque "tick" et notifier Angular.

{% hint style="info" %}
Zone.js est également utilisé pour reconstruire des "callstacks" d'appels asynchrones.
{% endhint %}

## Fonctionnement de la Change Detection

### 1. Déclenchement de la "change detection"

A la **fin de chaque "tick"** *(détecté grâce à Zone.JS)*, Angular déclenche la **"Change Detection" du "Root Component"**.

### 2. **"Change Detection" de chaque composant**

Le **"Change Detector"** du composant **compare les anciennes et les nouvelles valeurs** de chaque **expression** utilisée dans les **bindings** *(*[*Template Interpolation*](/angular/composants/template-interpolation) *ou* [*Property Binding*](/angular/composants/property-binding)*)*.

### 3. **Mise à jour de la vue si nécessaire**

En cas de changement, l'élément concerné est mis à jour dans la vue.

### 4. **Vérification récursive**

Le "Change Detector" de chaque "child component" est ensuite déclenché et l'étape 2 est reproduite pour chaque composant de façon récursive.

### 5. Double check

***\[Uniquement en mode développement]*** Angular relance l'intégralité de la "Change Detection" pour s'assurer que les valeurs retournées par les expressions ne changent pas.\
Cela permet de détecter les problèmes de conception ou d'implémentation tels que le changement du modèle par effet de bord ou les expressions dont le résultat est aléatoire.

![Angular Change Detection](/files/-LBXv0TCGziWI7PfaWf3)

{% tabs %}
{% tab title="1" %}
![](/files/-LOU4mFt-Ue5xtbIP4bQ)
{% endtab %}

{% tab title="2" %}
![](/files/-LOU4sC7Z_qVlI0JeCXI)
{% endtab %}

{% tab title="3" %}
![](/files/-LOU4vJ3leaK37oV-lru)
{% endtab %}

{% tab title="4" %}
![](/files/-LOU4xyi5vncho4pdsFY)
{% endtab %}

{% tab title="5" %}
![](/files/-LOU5-n8nAPjIdlWTmGs)
{% endtab %}

{% tab title="6" %}
![](/files/-LOU5CSG3ILDHvyBXNlV)
{% endtab %}

{% tab title="7" %}
![](/files/-LOU5HNTCnc2hsnaIip6)
{% endtab %}

{% tab title="8" %}
![](/files/-LOU5LprdaGPpXGZ_b-n)
{% endtab %}

{% tab title="9" %}
![](/files/-LOU5Ocq-rsPBNLLMDLU)
{% endtab %}

{% tab title="10" %}
![](/files/-LOU5RrTssIxY0YML6ND)
{% endtab %}

{% tab title="11" %}
![](/files/-LOU5UAirGzmRa1hrbM2)
{% endtab %}

{% tab title="12" %}
![](/files/-LOU5WjPJ4t0reJrd-BY)
{% endtab %}

{% tab title="13" %}
![](/files/-LOU5_UaEsWJgUoqvton)
{% endtab %}

{% tab title="14" %}
![](/files/-LOU5cEPDPq_Wv_52Etj)
{% endtab %}

{% tab title="15" %}
![](/files/-LOU5fRYhCXCMT2mBEbZ)
{% endtab %}
{% endtabs %}


# Optimisation de la Change Detection

Disponible prochainement.

En attendant, vous pouvez accéder à ce chapitre ici <http://courses.wishtack.com/angular/change-detection>.


# Immutabilité

Disponible prochainement.

En attendant, vous pouvez accéder à ce chapitre ici <http://courses.wishtack.com/angular/change-detection>.


# Quelques Liens

<https://blog.angularindepth.com/the-difference-between-ngdocheck-and-asyncpipe-in-onpush-components-4918ec4b29d4>

{% embed url="<https://blog.angularindepth.com/the-difference-between-ngdocheck-and-asyncpipe-in-onpush-components-4918ec4b29d4>" %}


# Project Structure & Modules


# Entry Point

Le point d'entrée d'une application Angular est situé par défaut dans le fichier `src/main.ts` *(configurable dans le fichier* `angular.json`*)*.

Grâce à Webpack, Angular suit les imports de fichiers à partir de ce point d'entrée pour construire les "bundles" JavaScript qui seront chargés dans le fichier `index.html` produit par le build.

`yarn build` => `ng build` => `webpack` => `dist/*`

{% embed url="<https://webpack.js.org/>" %}

![Webpack](/files/-LBuzHj8Gl9bKLn5mH6i)


# Définition d'un Module

Angular propose un concept de modules afin de mieux structurer le code et faciliter la réutilisation et le partage.

{% hint style="warning" %}
Attention à ne pas confondre les **modules Angular** avec les **modules ES2015 / TypeScript**.
{% endhint %}

Un module Angular est un mécanisme permettant de :

* regrouper des composants *(mais aussi des services, directives, pipes etc...),*
* définir leurs dépendances,
* et définir leur visibilité.

Un module Angular est défini simplement avec une classe *(généralement vide)* et le décorateur `NgModule`.

{% tabs %}
{% tab title="src/app/book/book.module.ts" %}

```typescript
import { NgModule } from '@angular/core';

import { PictureModule } from '../picture/picture.module';
import { BookPreviewComponent } from './book-preview/book-preview.component';

@NgModule({
    declarations: [
        BookPreviewComponent
    ],
    exports: [
        BookPreviewComponent
    ],
    imports: [
        HttpModule,
        PictureModule
    ]
})
export class BookModule {
}
```

{% endtab %}
{% endtabs %}

## `declarations`

Définit la liste des composants *(ou directives, pipes etc...)* contenus dans ce module.

## `exports`

Définit la liste des composants pouvant être utilisés par les modules qui importent celui-ci.

{% hint style="warning" %}
Les composants non-exportés ne peuvent être utilisés que par les composants contenus dans le module.
{% endhint %}

{% hint style="success" %}
Pour exporter un composant lors de sa génération par Angular CLI, il faut ajouter l'option `--export` : `yarn ng generate component --export book/book-preview`.

Pour éviter de lutter contre les oublis, une astuce consiste à **ajouter un "script" à votre fichier `package.json`** pour **unifier la façon de générer vos composants**.
{% endhint %}

{% tabs %}
{% tab title="package.json" %}

```javascript
{
    "scripts": {
        "generate:component": "ng generate component --export"
    }
}
```

{% endtab %}
{% endtabs %}

```bash
yarn generate:component book/book-preview
```

## `imports`

Définit la liste des dépendances du module. Il s'agit généralement de la liste des modules contenant les composants utilisés par les composants de la section [`declarations`](/angular/project-structure-and-modules/definition-dun-module#declarations).

![Angular Modules](/files/-LBvCVC22B-3Ls3_nyuC)

## Bonnes Pratiques

{% hint style="success" %}
La convention est de regrouper tous les composants dans le même dossier que le module.

```
book/
    book.module.ts
    book-preview/
        book-preview.component.ts
        book-preview.component.html
    ...
```

{% endhint %}

{% hint style="success" %}
Evitez les sous-arborescences *(définition de modules à l'intérieur d'autres modules etc...).*

**Flat is better than nested.**
{% endhint %}

## Angular CLI - Generate Module

Vous pouvez générer un module avec la commande suivante :

```bash
yarn ng generate module book
```

## Live Template

Pensez à utiliser des "live templates" sur votre IDE !

![Angular Module Live Template](/files/-LBvPr3qq28Zh4aJpoMi)


# Root Module

Une application Angular contient un seul et unique "root module" *(`AppModule` par défaut)*.

Le "root module" est un module classique dont la particularité est de définir le "root component" de l'application via la propriété `bootstrap`.

{% tabs %}
{% tab title="src/app/app.module.ts" %}

```typescript
@NgModule({

    declarations: [
        AppComponent
    ],
    imports: [
        BookModule
    ],
    bootstrap: [
        AppComponent
    ]
})
export class AppModule {
}
```

{% endtab %}
{% endtabs %}

> `bootstrap` est une liste car dans certains cas extrêmes, il est possible d'avoir plusieurs "root components".
>
> Une alternative à la propriété `bootstrap` est de surcharger la méthode `ngDoBootstrap`.

Le module `AppModule` est désigné comme "root module" via la ligne suivante du fichier `main.ts`.

{% tabs %}
{% tab title="src/main.ts" %}

```typescript
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
```

{% endtab %}
{% endtabs %}

Au démarrage de l'application, Angular recherche dans le DOM *(Cf. `src/index.html`)*, **le premier élément** correspondant au **sélecteur du composant** `AppComponent` *(`wt-app`)* et injecte alors le composant à cet endroit.


# Feature Module

Les composants *(ainsi que les directives, pipes, etc...)* sont **regroupés dans des modules par fonctionnalité**. Ces modules sont alors appelés "**feature module"**.

Il existe 5 familles de "feature modules" :

## Domain Feature Module

Ce module met à dispositions des modules qui l'importent un composant de type container qui représente une fonctionnalité entière. Exemple :

```typescript
@NgModule({
    declarations: [
        BookSearchComponent,
        BookSearchFormComponent
    ],
    exports: [
        BookSearchComponent
    ],
    imports: [
        BookModule
    ]
})
export class BookSearchModule {
}
```

## Service Feature Module

Un module qui ne met à disposition que des services *(e.g. : `HttpClientModule`)*. Cf. [Portée des Services](/angular/dependency-injection/portee-des-services).

## Widget Feature Module

Un module ne contenant quasiment que des composants *(généralement de type "presentational component") (e.g. `MaterialButtonModule`).*

{% hint style="success" %}
A moins de produire une librairie utilisée par plusieurs équipes *(librairie open-source)*, il est préférable d'exporter tous les composants pour éviter les mauvaises surprises d'export manquant lors de leur utilisation par les copains.
{% endhint %}

## Routed Feature Module

Un module contenant des composants associés à des routes. Cf. [Routing](/angular/routing).

Les modules de ce type sont généralement [Lazy Loaded](/angular/routing/lazy-loading).

## Routing Feature Module

Un simple module généralement associé à une [Routed Feature Module](/angular/project-structure-and-modules/feature-module#routed-feature-module) dont le but est simplement d'alléger la définition de ce dernier en prenant en charge la configuration du [routing](/angular/routing).


# Shared Module

Certains modules sont utilisés par quasiment tous les composants de l'application *(e.g.: `CommonModule`, `FlexLayoutModule`, `RouterModule`)*. Les importer dans chaque module peut s'avérer pénible.

Il est courant de factoriser ces imports en rassemblant toutes ces dépendances dans un module `SharedModule` importé par quasiment tous les autres modules de l'application.

{% tabs %}
{% tab title="src/app/shared/shared.module.ts" %}

```typescript
@NgModule({
    imports: SharedModule.MODULE_LIST,
    exports: SharedModule.MODULE_LIST
})
export class SharedModule {

    static readonly MODULE_LIST = [
        CommonModule,
        FlexLayoutModule,
        RouterModule
    ];

}
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Attention à ne pas trop surcharger ce module et en faire un "God Module".

N'y importez que les modules nécessaires pour quasiment tous les composants de l'application.
{% endhint %}


# Exemple

## Code Source

​<https://github.com/wishtack/wishtack-book-shop/tree/4-modules>

## Démo StackBlitz

{% embed url="<https://stackblitz.com/github/wishtack/wishtack-book-shop/tree/4-modules>" %}


# Dependency Injection

<br>


# Qu'est-ce que la "Dependency Injection" ?

La "Dependency Injection" **est un "design pattern"** qui consiste à **séparer l'instanciation** *(et donc l'implémentation)* d'une dépendance **et son utilisation**.

## Pourquoi ?

Ce "design pattern" permet :

* d'**inverser les dépendances**,
* d'**éviter le couplage fort** avec les dépendances,
* de **factoriser l'instanciation** d'une dépendance,
* de **faciliter le remplacement** d'une dépendance par une autre implémentation **à des fins fonctionnelles ou de "testing“**.

## Comment ?

### Sans "Dependency Injection"

![Without Dependency Injection](/files/-LCExm1N-SKFyQdW7pKV)

### Dependency Inversion

![Dependency Inversion](/files/-LCExpXFgv_jAODr1i01)

### Dependency Injection

![Dependency Injection](/files/-LCExtIt1Ns6QTbh65PI)


# Injection d'un Service Angular

## Qu'est-ce qu'un service Angular ?

Avec Angular, **une dépendance** est généralement l'instance d'une classe permettant de **factoriser certaines fonctionnalités** ou d'**accéder à un état** permettant ainsi aux composants de communiquer entre eux.

Dans le vocabulaire Angular, ces classes sont appelées "**services"**.

Les services sont le plus souvent **des singletons**. Cf. [Portée des Services](/angular/dependency-injection/portee-des-services).

## Injection d'un service Angular

Un service Angular peut être injecté par n'importe quelle classe Angular *(i.e. : composant,* [*Directive*](/angular/directives)*,* [*Service*](/angular/dependency-injection/services-and-providers) *ou* [*Pipe*](/angular/pipes)*)* via les paramètres de son constructeur.

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
@Component({
    ...
})
export class BookPreviewComponent {

    constructor(private _httpClient: HttpClient) {
    }

}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Vous remarquerez l'utilisation des [TypeScript Parameter Properties](/typescript/typing-des-proprietes#raccourci-pour-les-parametres-ordonnees-du-constructeur) afin de copier le service `HttpClient` dans la propriété `_httpClient`.
{% endhint %}


# Services & Providers

## Déclaration d'un Service

Pour déclarer un service Angular, il suffit de créer une classe TypeScript et de la décorer avec le décorateur `@Injectable()`.

{% tabs %}
{% tab title="book-repository.ts" %}

```typescript
@Injectable()
export class BookRepository {
    ...
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
N'oubliez pas les parenthèses du décorateur `@Injectable()`.
{% endhint %}

> Pensez à utiliser un template ou live template dans votre IDE ou encore Angular CLI :\
> `yarn ng generate module book-repository`

{% hint style="success" %}
Evitez de suffixer tous vos services et leurs fichiers respectivement avec les suffixes `Service` et `.service.ts`. tant qu'il n'y a pas de conflit ou d'ambiguïté.

En suffixant toutes les classes et instances par `Service`, on finit par perdre en lisibilité.

Une classe *(de type "helper" par exemple)* peut devenir un "service" ou cesser d'être un "service" du jour au lendemain, la frontière est fine.
{% endhint %}

{% hint style="success" %}
La bonne pratique est de **toujours ajouter le décorateur** `@Injectable()` bien que celui-ci ne soit actuellement pas obligatoire tant que le service n'a pas de dépendances.
{% endhint %}

En essayant de l'injecter,

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
@Component({...})
export class BookPreviewComponent {
    constructor(private _bookRepository: BookRepository) {
    }
}
```

{% endtab %}
{% endtabs %}

... vous remarquerez l'erreur suivante :

```
StaticInjectorError(AppModule)[BookPreviewComponent -> BookRepository]: 
  StaticInjectorError(Platform: core)[BookPreviewComponent -> BookRepository]: 
    NullInjectorError: No provider for BookRepository!
```

Angular essaie donc d'injecter une instance de `BookRepository` mais ne sait pas la produire.

## Définition d'un Provider

Afin de pouvoir instancier un service, **Angular a besoin d'un "provider"** lui indiquant **comment produire l'instance** de ce service.

Cela se fait généralement via la **propriété `providers` de la configuration du module associé** mais nous verrons d'autres façons de faire plus tard. Cf. [Portée des Services](/angular/dependency-injection/portee-des-services) et [Tree-Shakable Services](/angular/dependency-injection/tree-shakable-services).

### `useClass`

La façon la plus commune de définir un provider est la suivante :

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
    providers: [
        {
            provider: BookRepository,
            useClass: BookRepository
        }
    ]
})
export class BookCoreModule {
}
```

{% endtab %}
{% endtabs %}

Cela veut dire que pour fournir une instance de la classe `BookRepository`, il suffit de l'instancier en lui passant en paramètre les dépendances dont elle a besoin. Cf. [Injection d'un Service Angular](/angular/dependency-injection/injection-dun-service-angular).

Cette approche étant la plus commune, il est alors recommandé d'utiliser la syntaxe raccourcie suivante :

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
    providers: [
        BookRepository
    ]
})
export class BookCoreModule {
}
```

{% endtab %}
{% endtabs %}

### `useValue`

Utilisation d'une valeur "hardcoded".

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
    providers: [
        {
            provider: BookRepository,
            useValue: new FakeBookRepository()
        }
    ]
})
export class BookCoreModule {
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Il est préférable d'éviter cette utilisation.

Dans le cas d'une classe, cela voudrait dire que l'objet est instancié avant le démarrage de l'application et pourrait ne jamais être utilisé.

Dans le cas d'une constante, cf. [Class vs Injection Token](/angular/dependency-injection/class-vs-injection-token).
{% endhint %}

### `useFactory`

Comme son nom l'indique, cette propriété permet de définir l'instanciation du service via une fonction.

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
    providers: [
        {
            provider: BookRepository,
            useFactory: () => {

                /* Useful for A/B testing. */
                if (isBTeam) {
                    return new BookRepositoryV2();
                }

                return new BookRepository();

            }
        }
    ]
})
export class BookCoreModule {
}
```

{% endtab %}
{% endtabs %}


# Portée des Services

## Injector Tree

Angular ne dispose pas que d'un seul "injector" mais d'un arbre d'"injectors".

### Root Injector

Tous **les "providers" définis par les modules importés directement ou indirectement par l'`AppModule`** sont injectés par le "root injector" et **sont donc accessibles dans toute l'application**.

![Angular Services Scope](/files/-LCYMnFNv_-9ydBMXgFF)

### Component Injector

Chaque composant dispose d'un "injector" et peut définir des "providers" via la propriété `providers` de sa configuration.

Ces "providers" vont écraser les "providers" parents *(ceux des composants parents ou du "root injector")* et définir **de nouvelles instances des services associés pour chaque instance du composant**.

{% tabs %}
{% tab title="book-preview\.component.ts" %}

```typescript
@Component({
    providers: [
        BookRepository
    ]
})
export class BookPreviewComponent {
}
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Il est préférable d'éviter cette approche et d'utiliser des [Inputs / Outputs pour interagir avec les "child components"](/angular/interaction-entre-composants). Autrement, les composants auront des comportements **différents et parfois imprédictibles** en fonction de l'endroit où ils sont utilisés.
{% endhint %}

### Lazy Loading

Dans le cas du [Lazy Loading](/angular/routing), les modules Angular sont chargés de façon asynchrone et afin d'éviter de perturber le "root injector", ces derniers créent des "child injectors".

{% hint style="warning" %}
Le danger dans ce cas est de réimporter un module définissant des "providers" et on se retrouve alors avec plusieurs instances du même "service".

Cela pose particulièrement problème pour les "**stateful services**".
{% endhint %}

## Feature Service Module

{% hint style="success" %}
Pour éviter le problème de double instanciation de "**stateful services**" décrit [ci-dessous](/angular/dependency-injection/portee-des-services#lazy-loading), la bonne pratique la plus commune est de créer **des modules dédiés** à la définition des "providers" de "**stateful services**" qui seront ensuite importés uniquement par le "root module" `AppModule`.
{% endhint %}

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
    providers: [
        BookRepository
    ]
})
export class BookCoreModule {
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="app.module.ts" %}

```typescript
@NgModule({
    imports: [
        BookCoreModule
    ]
})
export class AppModule {
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Cette approche reste fragile car à la moindre inattention, le module `BookCoreModule` peut se retrouver importé *(directement ou indirectement)* par d'autres modules "lazy loaded" et le même problème se reproduira.
{% endhint %}

## `Module.forRoot`

{% hint style="success" %}
La bonne pratique est de ne pas définir les "providers" de "stateful services" dans le module mais plutôt via une méthode statique conventionnellement nommée `forRoot` dont le résultat est importé par le "root module" `AppModule`.
{% endhint %}

{% tabs %}
{% tab title="book-core.module.ts" %}

```typescript
@NgModule({
})
export class BookCoreModule {

    static forRoot(): ModuleWithProviders {
        return {
            ngModule: BookCoreModule,
            providers: [
                BookRepository
            ]
        };
    }

}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="app.module.ts" %}

```typescript
@NgModule({
    imports: [
        BookCoreModule.forRoot()
    ]
})
export class AppModule {
}
```

{% endtab %}
{% endtabs %}

Les modules "lazy loaded" qui importent le module `BookCoreModule` ne produiront alors pas de doublon.

Cette approche est malheureusement encore loin d'être parfaite :

* la syntaxe `forRoot` est overkill ;
* rien n'empêche l'import `BookCoreModule.forRoot()` de dans un module autre que le "root module" ;
* tous les "stateful services" sont importés par le "root module" dès le chargement de l'application et peut-être inutilement.

C'est pour ces raisons qu'Angular 6 a introduit la notion de [Tree-Shakable Services](/angular/dependency-injection/tree-shakable-services).


# Tree-Shakable Services

Pour éviter les problèmes décrits précédemment *(Cf.* [*Portée des Services*](/angular/dependency-injection/portee-des-services)*)* et les solutions complexes associées, **depuis Angular 6, il n'est plus nécessaire de définir les "providers" au niveau des modules**.

## Bye `providers`, welcome `providedIn`!

Désormais, les définitions du "provider" et de la portée du service peuvent se faire directement au niveau du décorateur `@Injectable` du service.

Il suffit alors de déclarer un service de cette façon :

{% tabs %}
{% tab title="book-repository.ts" %}

```typescript
@Injectable({
    providedIn: 'root'
})
export class BookRepository {
}
```

{% endtab %}
{% endtabs %}

... pour pouvoir ensuite l'injecter partout dans l'application.

{% hint style="info" %}
`providedIn: 'root'` indique que le provider est ajouter au "root injector".

Il est également possible de sélectionner un module *(`providedIn: BookCoreModule`)* pour que le service ne soit disponible que si le module associé est importé.
{% endhint %}

Il est ensuite possible de personnaliser l'instanciation avec `useValue`, `useClass`, `useFactory` etc...

{% tabs %}
{% tab title="book-repository.ts" %}

```typescript
@Injectable({
    providedIn: 'root',
    useFactory: () => {
        return new BookRepository();
    }
})
export class BookRepository {
}
```

{% endtab %}
{% endtabs %}

## Avantages

Cette approche a pour avantage :

* d'être plus simple et rapide à implémenter,
* de ne plus avoir à mettre tous les services dans des modules *(historiquement, on finissait souvent avec un service / un module),*
* de **ne charger le code des services qu'à la première injection** ***(lazy loading)***.
* et surtout de permettre le **Tree-Shaking des services non utilisés**.

## Tree Shaking

Grâce à cette approche, il n'est plus nécessaire de charger le code des services dès le chargement de l'application et par conséquent **si le service n'est jamais utilisé, le code du service sera entièrement retiré du build final de l'application**.


# Class vs Injection Token

Une dépendance peut être de type `string` ou "objet literal" ou même sans type. Cf. <https://angular.io/api/core/InjectionToken>.

{% hint style="danger" %}
Pour des raisons de lisibilité et de maintenabilité, évitez l'utilisation d'`InjectionToken` et préférez l'utilisation de classes.
{% endhint %}

Au lieu de :

```typescript
export const API_BASE_URL = new InjectionToken<string>('ApiBaseUrl');

@NgModule({
    providers: [
        {
            provide: API_BASE_URL,
            useValue: 'https://api.wishtack.io/api/v2'
        }
    ]
})
export class AppModule {
}

@Component({
    ...
})
export class AppComponent {

    constructor(@Inject(API_BASE_URL) apiBaseUrl: string) {
    }
    
}
```

... préférez donc l'approche ci-dessous afin de bénéficier sans effort du "typing" TypeScript et éviter l'utilisation du décorateur `@Inject()`.

```typescript
export class Config {
    readonly apiBaseUrl: string;    
}

@Injectable()
export class CustomConfig extends Config {
    apiBaseUrl = 'https://api.wishtack.io/api/v2';
}

@NgModule({
    providers: [
        {
            provide: Config,
            useClass: CustomConfig
        }
    ]
})
export AppModule {
}

@Component({
    ...
})
export class AppComponent {
​
    constructor(config: Config) {
        this.apiBaseUrl = config.apiBaseUrl;
    }
    
}
```


# Exemple

## Code Source

<https://github.com/wishtack/wishtack-book-shop/tree/5-service>

## Démo StackBlitz

{% embed url="<https://stackblitz.com/github/wishtack/wishtack-book-shop/tree/5-service>" %}


# Callback Hell vs. Promise vs. Async / Await

Dans un univers asynchrone, nous sommes amenés à utiliser des fonction de "callback" pour être informé du résultat du traitement.

Le chapitre [Callback Hell](/angular/callback-hell-vs.-promise-vs.-async-await/callback-hell) présente les limitations des "callback" "classiques" puis les chapitres suivants présentent progressivement des solutions de plus en plus élégantes et puissantes.

{% hint style="info" %}
Bien qu'il soit tentant d'aller directement au chapitre [Observables](/angular/observables), il est nécessaire de maitriser les [Promise](/angular/callback-hell-vs.-promise-vs.-async-await/promise) ainsi que l'approche [Async / Await](/angular/callback-hell-vs.-promise-vs.-async-await/async-await) qui s'avèrent pratiques dans certains cas.
{% endhint %}

{% content-ref url="/pages/-LCYyXMyzj6Tum4oNbMh" %}
[Callback Hell](/angular/callback-hell-vs.-promise-vs.-async-await/callback-hell)
{% endcontent-ref %}

{% content-ref url="/pages/-LCYzaXEXLJJZDb27ppZ" %}
[Promise](/angular/callback-hell-vs.-promise-vs.-async-await/promise)
{% endcontent-ref %}

{% content-ref url="/pages/-LCYzh\_TfeAiXWXU9T2J" %}
[Async / Await](/angular/callback-hell-vs.-promise-vs.-async-await/async-await)
{% endcontent-ref %}

{% content-ref url="/pages/-LCZ-2sphpWK1Mgz0K4m" %}
[Observables](/angular/observables)
{% endcontent-ref %}


# Callback Hell

Supposons deux fonctions asynchrones :

```typescript
getCurrentCity(callback: (error, city: string) => void);
```

et

```typescript
getWeatherInfo(city: string, callback: (error, weatherInfo: WeatherInfo) => void);
```

Avec des "callbacks" classiques, nous sommes amenés à utiliser ces fonctions de la façon suivante :

```typescript
const handleError = error => {
    console.error(`Something went wrong but I don't know how to handle it`);
};

getCurrentCity((error, city) => {

    if (error != null) {
        handleError(error);
        return;
    }
    
    getWeatherInfo(city, (err, weatherInfo) => {
        
        if (error != null) {
            handleError(error);
        }
        
        console.log(`${city}: ${weatherInfo.temperature}`);
        
    });
    
});
```

Ce cas est relativement simple mais manque déjà en lisibilité et **présente déjà quelques erreurs**.

### Closure Cupide

A la ligne 12, le paramètre d'erreur a été nommé `err` mais à la ligne 14, c'est le paramètre `error` du closure parent qui est utilisé.

Plus les "callbacks" se cascadent plus ce type d'erreur a de chance de se produire.

### Multi-purpose Error-First Callback

Le problème avec l'approche **Error-First Callback** utilisée dans notre exemple en suivant les conventions NodeJS, est que la même "callback" sert à deux finalités : le succès et l'échec ; avec cette approche, il arrive souvent d'omettre la gestion d'erreur et donc d'appeler l'étape suivante malgré tout.

C'est le cas de notre exemple où il nous manque un `return` après la ligne 15.

### Pas de `catch`

Il est nécessaire d'appeler de capturer et gérer les erreurs de chaque appel.

### Annulation

Il est impossible dans l'état d'annuler l'enchainement des traitements une fois lancé.

### JavaScript Async Libraries

Bien sûr, il existe des librairies "cache-misère" telles que <http://caolan.github.io/async/> mais elles sont progressivement abandonnées pour passer aux approches abordées dans les chapitres suivants.


# Promise

Le concept de **`Promise`** date des années 70. Les **`Promise`**&#x73; sont parfois appelés **`Future`** ou **`Deferred`**.

En 2010, le développeur Kris Kowal inspire la communauté JavaScript en implémentant ce concept pour NodeJS via la librairie Q <https://github.com/kriskowal/q>. Les implémentations se sont ensuite démultipliées jusqu'à ce que les `Promise`s deviennent un standard avec ES6.

## Fonctionnement des `Promise`s

Le fonctionnement d'une `Promise` est généralement le suivant :

1\. Un appelant fait appel à une fonction qui procède à un traitement asynchrone mais **retourne de façon synchrone** un objet container ***(la `Promise`)*** à l'appelant.

2\. A ce stade, la `Promise` est le plus souvent dans un état "pending".

3\. L'appelant inscrit sur la `Promise` une "callback" de succès pour être informé quand le résultat est disponible (e.g. : `promise.then(data => console.log(data)`) et une callback d'erreur pour être informé de l'échec (e.g. : `promise.catch(error => console.error(error)`).

4\. Quand la fonction appelée obtient le résultat *(ou une erreur)*, elle notifie la `Promise` qui passe alors à un état "resolved" *(ou "rejected")*.

5\. La `Promise` déclenche alors toutes les fonctions de "callback" de succès *(ou d'erreur)* qui ont pu lui être transmises (via les méthodes `.then` et `.catch`).

![How Promise works](/files/-LCZLJP2lfCNfkiDvWQO)

## Consommation d'une `Promise`

A titre d'exemple, nous allons utiliser la fonction `fetch` désormais standard qui vient déloger la poussiéreuse `XMLHttpRequest`.

Cette fonction a la particularité de retourner une `Promise`.

```typescript
fetch('https://www.googleapis.com/books/v1/volumes?q=extreme%20programming')
    .then(response => {
        console.log(response.status);
    });
```

Pour accéder au "body" de la response, il faut utiliser la méthode `Response.json` qui retourne une `Promise` également.

```typescript
fetch('https://www.googleapis.com/books/v1/volumes?q=extreme%20programming')
    .then(response => {
        
        response.json()
            .then(data => {
                console.log(data.totalItems);
            });
        
    });
```

Malheureusement, pour le moment, les problèmes associés au "callbacks waterfall" persistent.

## Chaînage de `Promise`s

Pour éviter les "callbacks waterfall", il est possible de chainer les `Promise`. En effet, les méthodes `Promise.then` et `Promise.catch` retournent des `Promise`s.

### Chaînage synchrone

```typescript
fetch('https://www.googleapis.com/books/v1/volumes?q=extreme%20programming')
    .then(response => response.status)
    .then(status => console.log(status);
```

La ligne 1 retourne une `Promise` contenant la `Response` *(elle est donc de type `Promise<Response>`)*.

La ligne 2 crée à son tour une nouvelle `Promise` déduite de la première et contenant le "status" *(elle est donc de type `Promise<number>`).*

La ligne 3 consomme donc le résultat de la `Promise` précédente.

### Chaînage asynchrone

Une autre propriété intéressante des `Promise` est que **si la valeur retournée** à l'une des étapes **est une `Promise`**, alors l'étape suivante **ne sera appelée que quand la `Promise` sera "resolved"** et elle recevra en paramètre le résultat de résolution.

```typescript
fetch('https://www.googleapis.com/books/v1/volumes?q=extreme%20programming')
    .then(response => response.json())
    .then(data => console.log(totalItems);
```

### Chaînage et gestion d'erreurs

Si une erreur se produit à n'importe quelle étape soit car :

* la `Promise` initiale est "rejected",
* l'une des étapes lève une exception,
* ou l'une des étapes retourne une `Promise` "rejected,

alors **toutes les étapes suivantes sont ignorées** et la "callback" associée au premier "catch" de la chaîne *(à partir de l'erreur)* est appelé.

```typescript
fetch('https://www.googleapis.com/books/v1/volumes?q=extreme%20programming')
    .then(response => response.JSON())
    .then(data => console.log(totalItems))
    .catch(error => console.error(error));
```

Nous obtenons alors l'erreur `response.JSON is not a function` à la ligne 4 *(car la méthode se nomme `json` et non `JSON`)*.

## Création de `Promise`s

Pour créer une `Promise`, il faut instancier la classe `Promise` tel qu'indiqué ci-dessous et appeler la fonction "resolve" avec la donnée de résolution en cas de succès ou la méthode "reject" avec l'objet d'erreur en cas d'échec.

```typescript
const getCurrentCity = () => {

    return new Promise((resolve, reject) => {
    
        /* Simulating some async stuff... */
        setTimeout(() => {
        
            if (hasPermission !== true) {
                reject(new Error('Permission denied!'));
                return;
            }
            
            resolve('Lyon');
        
        }, 1000);
    
    });

};

getCurrentCity()
    .then(city => getWeatherInfo(city))
    .then(weatherInfo => console.log(weatherInfo.temperature))
    .catch(error => console.error(error));
```

{% hint style="warning" %}
Une `Promise` ne peut être "resolved" ou "rejected" qu'une seule fois.

**C'est le premier appel qui gagne** et qui définit donc l'état final de la `Promise`, les appels suivants sont simplement ignorés.
{% endhint %}

> Vivement le jour où les appels superflus de "resolve" et "reject" déclencheront des erreurs.

## Limitation du Scope

Parmi d'autres limitations que nous aborderons plus tard, dans le dernier exemple, on peut remarquer l'indisponibilité de la variable `city` lors du `console.log` de l'étape finale *(ligne 23)*.

Il existe bien sûr des solutions de contournement mais peu séduisantes. Il est préférable d'adopter directement l'approche [Async / Await](/angular/callback-hell-vs.-promise-vs.-async-await/async-await) pour éviter ces problèmes.


# Async / Await

Les [Promises](/angular/callback-hell-vs.-promise-vs.-async-await/promise) sont intéressantes mais loin d'être aussi claires que du code synchrone.

## Le rêve

Dans le dernier exemple du chapitre précédent, nous obtenons le résultat suivant :

```typescript
getCurrentCity()
    .then(city => getWeatherInfo(city))
    .then(weatherInfo => console.log(weatherInfo.temperature))
    .catch(error => console.error(error));
```

... qui est intéressant mais loin d'être aussi clair que du code synchrone :

```typescript
try {

    const city = getCurrentCity();
    const weatherInfo = getWeatherInfo(city);
    
    console.log(weatherInfo.temperature);
    
}
catch (error) {
    console.error(error);
}
```

## Fonctionnement d'Async / Await

L'approche "async / await" est un concept introduit en ECMAScript 7 *(ou ES2016)* et inspiré du C# *(on le retrouve dans les langages les plus cools du marché tels que Python).*

"async / await" est une **"syntactic sugar" autour des `Promise`s** permettant d'**indiquer explicitement** les endroits où une **fonction asynchrone attend le résultat d'une `Promise`**.\
En arrivant à ces endroits, un tick est déclenché et **l'"event loop" reprend la main**.\
Quand la **`Promise`** est "resolved", la fonction reprend son exécution à l'endroit où elle a été interrompue dès que possible *(i.e. : une fois que toutes les tâches empilées dans la "queue" de l'"event loop" sont dépilées)*.

On obtient alors le résultat suivant :

```typescript
const getCurrentCity = () => {
    /* Return an already resolved promise. */
    return Promise.resolve('Lyon');
};

const getWeatherInfo = (city) => {
    /* Return an already resolved promise. */
    return Promise.resolve({
        temperature: city.length * 6
    });
};

const main = async () => {

    try {

        const city = await getCurrentCity();
        const weatherInfo = await getWeatherInfo(city);

        console.log(`${city}: ${weatherInfo.temperature}`);

    }
    catch (error) {
        console.error(error);
    }

};

main()
    .then(() => console.log('Finished'))
    .catch(() => console.error('Failed!'));
```

{% hint style="warning" %}
`await` ne peut être utilisé que dans une fonction `async`.
{% endhint %}

{% hint style="info" %}
Une fonction `async` peut être appelée depuis n'importe quelle fonction et son type de retour sera une **`Promise`**.
{% endhint %}


# Observables

La combinaison des [Promises](/angular/callback-hell-vs.-promise-vs.-async-await/promise) avec [Async / Await](/angular/callback-hell-vs.-promise-vs.-async-await/async-await) est intéressante mais ne répond pas encore à tous les "use cases".

Pour outrepasser les limites des "promises" pour les traitements asynchrones, Angular se base principalement sur le concept d'**`Observables`** ou plus généralement le **Reactive Programming**.\
En attendant la standardisation des Observables, Angular utilise la **librairie RxJS**.

{% content-ref url="/pages/-LC\_B0kEnl87LKRdhOzZ" %}
[Reactive Programming](/angular/observables/reactive-programming)
{% endcontent-ref %}

{% content-ref url="/pages/-LC\_BGJPioB\_yXtwcoE7" %}
[Promise vs Observable](/angular/observables/promise-vs-observable)
{% endcontent-ref %}


# Reactive Programming

L'idée du Reactive Programming est d'adopter au maximum une **approche déclarative plutôt qu'impérative.**

Le Reactive Programming permet de **réduire le couplage entre les actions, les données, les traitements et le résultat des traitements**. Cela améliore également la **résilience** et la "**scalability**" de l'architecture.

Le succès de cette approche est fortement lié à l'émergence des **Microservices**, des technologies telles que **Kafka Streams**, en frontend à des frameworks tels qu'Angular et à des plateformes de streaming telles que Netflix.

![Reactive Programming Trends](/files/-LC_0irDhZE5_BCK0Gne)

## The Reactive Manifesto

{% embed url="<https://www.reactivemanifesto.org/>" %}
The Reactive Manifesto
{% endembed %}

<https://www.reactivemanifesto.org/>

## Exemple d'approche impérative

1. Les ordres de virement bancaire sont stockées dans une base de données.
2. &#x20;Un "batch" est lancé chaque soir pour déclencher les virements.
3. Un "batch" est lancé quelques heures plus tard pour notifier les utilisateurs en fonction des résultats stockés par le "batch" précédent.

## Exemple d'approche réactive

1. L'utilisateur s'inscrit aux notifications de virements.
2. Le système de notification est donc inscrit au flux de résultats de traitement de virements.
3. Le flux de résultats de traitement est produit par l'inscription au flux d'ordres de virements.
4. Le flux d'ordres de virements est alimenté par différentes sources.

## ReactiveX

L'implémentation la plus commune des `Observable`s est ReactiveX et elle est disponible dans différents langages : **RxJS**, **RxPython**, **RxJava**, **Rx.NET** etc...

{% embed url="<http://reactivex.io/>" %}

##


# Promise vs Observable

| `Promise`                              | `Observable`                                                                   |
| -------------------------------------- | ------------------------------------------------------------------------------ |
| Produit une seule valeur.              | Produit un "stream" de valeurs *(potentiellement infini)*.                     |
| Non annulable.                         | Annulable.                                                                     |
| Traitement immédiat.                   | Lazy : le traitement n'est déclenché qu'à la première utilisation du résultat. |
| Deux méthodes uniquement (then/catch). | Une centaine d'opérateurs de transformation natifs.                            |




---

[Next Page](/llms-full.txt/1)

