---
title: Développer une application Angular
tags: frontend, support, angular
robots: noindex, nofollow
author: Julien Noyer
---
# Développer une application Angular

## Introduction
[Angular](https://angular.io) (communément appelé Angular 2+ ou Angular v2 et plus) est un cadriciel (framework) côté client, open source, basé sur [TypeScript](https://www.typescriptlang.org), et co-dirigé par l'équipe du projet « Angular » à Google et par une communauté de particuliers et de sociétés. Angular est une réécriture complète de AngularJS, cadriciel construit par la même équipe.
<br>
## Environement de travail
La méthode la plus communément utilisée pour développer des applications avec Angular est celle qui consiste à utiliser une suite de [CLI](https://fr.wikipedia.org/wiki/Interface_en_ligne_de_commande) nommée [AngularCLI](https://cli.angular.io) qu'il est possible d'installer avec la commande suivante :
```
sudo npm install -g @angular/cli
```
> Le préfixe `sudo` obligatoire dans certains cas pour installer le module en global.
Les instructions qui suivents dans ce documents sont baséee sur l'utilisation de [AngularCLI](https://cli.angular.io) pour simplifier la démonstration des notions qu'il aborde.
---
<br><br><br><br><br><br>
# Créer et structurer une application Angular
*One framework. Mobile & desktop.*

## Création de l'application Angular
Pour bien débuter le développement d'une application avec [Angular](https://angular.io) il est important de respecter une structure et de correctement organiser les différents [composants](https://angular.io/guide/architecture-components) qui seront utilisés dans l'application. Il est donc important de sélectionner un dossier spécifique sur la machine locale dans laquelle sera développée l'application [Angular](https://angular.io).
Nous sélectionnons le dossier contenant nos différrentes projets pour y créér notre application [Angular](https://angular.io) avec la commande suivante :
```
ng new ANGapp
```
Au lancemant de cette commande une prémière question nous est posée, il nous est demandé si nous souhaitons ou nous ajouter un [routeur](https://angular.io/guide/router) :
```
? Would you like to add Angular routing? (y/N)
```
Nous n'ajoutons pas de [routeur](https://angular.io/guide/router) et nous passons à l'étape suivante qui nous demande de sélectionner le format de style que nous souhaitons utilisé :
```
? Which stylesheet format would you like to use? (Use arrow keys)
```
Nous sélectionnons CSS puis nous validons notre choix. Suite à cette dernière commande, tous les packages nécessaires à l'utilisation de [Angular](https://angular.io) sont installés, nous nous plaçons ensuite dans le dossier qui vient d'être créé :
```
cd ANGapp
```
<br>
## Gestion des routes de l'application
Contrrairement à des routes serveur, celles utilisées dans [Angular](https://angular.io) n'ont pas pour but de correspondre à des requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) mais plutôt d'utiliser les [endpoints](https://en.wikipedia.org/wiki/Web_API) des URL, pour afficher dans le [DOM](https://fr.wikipedia.org/wiki/Document_Object_Model) un composant spécifique. Dans la mesure ou une application [Angular](https://angular.io) s'affiche dans une seule est unique page HTML, il s'agit donc d'être capable d'afficher les bons élements du [DOM](https://fr.wikipedia.org/wiki/Document_Object_Model) pour la bonne URL.
Avant de configurer le [routeur](https://angular.io/guide/router) de notre application, nous allons dans un premier temps créer des [composants](https://angular.io/guide/architecture-components) qui correspondront au différerntes routes de notre application. il est important de noter à cette étape que nous considérons le composant d'une route comme un `composant principal` dans lequel nous intégrerons d'autres composants.
Avant de créer nos [composants](https://angular.io/guide/architecture-components) principaux nous allons structurer notre application en créant dans le dossier `app` un dossier nommé `routes` dans lequel nous crérons les [composants](https://angular.io/guide/architecture-components) des routes :
```
mkdir src/app/routes
```
Une fois ce dossier créé nous pouvonos générer nos [composants](https://angular.io/guide/architecture-components) principaux avec la commande suivante :
```
ng g c routes/homePage -is
```
> Le drapeau `-is` permet de créer un composant sans y ajouter de CSS.
Cette commande à créée trois différents fichiers qui nous seront ensuite utiles pour définir le contenu de notre page d'accueil. Nous répettons à présent la commandes précédente pour avoir en tout les dossier suivants dans le dossier `routes` :
- connected-page pour un utilisateur connecté
- home-page pour afficher une page d'acceil
Le contenu des fichiers de nos différrents [composants](https://angular.io/guide/architecture-components) principaux seront éditer plus tard, nous allons avant cela configurer le module de [routing][https://angular.io/guide/router] que nous allons utiliser dans notre application. Ce fichier à pour but de distruber le bon [composant](https://angular.io/guide/architecture-components) principal selon les routes que nous allons définir.
Nous créons donc un fichier nommé `app.router.ts` à la racine de notre dossier `app` :
```
touch src/app/app.router.ts
```
Nous ouvrons ce fichier et y collons le code qui permet d'initier notre module de [routing][https://angular.io/guide/router] :
```typescript=
/*
Imports
*/
// Angular
import { Routes } from '@angular/router';
//
/*
Export
*/
export const AppRouterModule: Routes = [];
//
```
Dans un premier temps nous importons la class `Routes` dans le fichier ce qui nous permet ensuite d'exporter une constante nommée `AppRouterModule` que nous typons en utilisant la class `Routes` en définition.
Nous pouvons à présent importer le composant principal `HomePageComponent` après l'import de la class `Routes` :
```typescript=
// Inner
import { HomePageComponent } from "./routes/home-page/home-page.component";
```
Nous importons le [composant](https://angular.io/guide/architecture-components) principal dans notre module de [routing][https://angular.io/guide/router] car nous en avons besoin pour l'ajouter dans notre constante `AppRouterModule`. Avec [Angular](https://angular.io) pour définir une route il suffit d'ajouter dans la constante `AppRouterModule` un objet pour chaque route en définissant les propriétés suivantes :
```typescript=
{
path: '',
component: HomePageComponent
}
```
Nous allons donc à présent importer les autres [composants](https://angular.io/guide/architecture-components) principaux dans notre module de [routing][https://angular.io/guide/router] de telle sorte à configurer les routes suivantes :
- path `''` avec le compsant `HomePageComponent`
- path `'connected'`avec le composant `ConnectedPageComponent`
A la fin de cette étapes toutes nos routes sont configurées dans le module de [routing][https://angular.io/guide/router] de notre application, nous devons à présent l'intégrer dans le fichier `app.module.ts`. D'ordre général avec [Angular](https://angular.io) lorsque un module est créé il est obligatoire de le référencer dans le fichier `app.module.ts` pour quil soit disponible dans l'application.
Nous ouvons donc le fichier `app.module.ts` pour y importer la class `RouterModule` et notre module de [routing][https://angular.io/guide/router] :
```typescript=
// Router
import { RouterModule } from "@angular/router"
import { AppRouterModule } from "./app.router";
```
Nous pouvons à présent ajouter un item dans le tableau des imports du `@NgModule` :
```typescript=
...
imports: [
BrowserModule,
RouterModule.forRoot( AppRouterModule, { onSameUrlNavigation: 'reload' } ),
]
...
```
La configuration du module de [routing][https://angular.io/guide/router] de notre application est à présent terminé, il nous reste à éditer le fichier `app.component.ts` pour y intégrer la gestion de nos routes :
```typescript=
/*
Import
*/
// Angular
import { Component, OnInit } from '@angular/core';
//
/*
Componant configuration
*/
@Component({
selector: 'app-root',
template: `
<router-outlet></router-outlet>
`
})
//
/*
Componant class definition
*/
export class AppComponent implements OnInit {
constructor(){}
ngOnInit(){}
}
//
```
Nous pouvons a présent lancer la commande qui nous permet d'afficher l'application dans notre navigateur à l'adresse http://localhost:4200 :
```
ng serve
```
> Cette commande sert uniquement en phase de développement pour tester l'application.
<br>
## Création de composants enfants
Comme vu à l'étape précédente, un [composant](https://angular.io/guide/architecture-components) [Angular](https://angular.io) est constitué de plusieurs fichiers différents mais qu'il est important de définir les rôles qu'ils doivent jouer dans une application. Dans un premier temps nous avons vu les `composants principaux` que nous utilisons dans nos routes, nous allons à présent aborder les `composants enfants`.
Le rôle de des [composants](https://angular.io/guide/architecture-components) enfants est d'être intégres dans des [composants](https://angular.io/guide/architecture-components) principaux dans le but de les découper pour mieus les structurer.
Nous allons tout d'abord créer celui qui nous permettra de naviguer de route en route dans notre application, à savoir la navigations. Dans un premier temps nous créons un dossier à la racine du dossier `app` dans lequels nous crérons nos composants enfants :
```
mkdir src/app/shared
```
Une fois le dossier créer nos générons le [composant](https://angular.io/guide/architecture-components) enfant pour la navigation avec la commande suivnate :
```
ng g c shared/header -is
```
Il suffi à présent d'ajouter des balises HTML dans le fichier `header.component.html`, nous allons lui donner l'aspect d'un header HTML classique puis nous y intégrons des liens vers les endpoints que nous avons défini dans le fichier `app.router.ts` de la façon suivante :
```htmlmixed=
<a [routerLink]="'/'">Home</a>
```
Nous utilisons ici la directive [routerLink](https://angular.io/api/router/RouterLink) qui est disponible grâce à la configuration de notre [**rRouteur** https://angular.io/guide/router](https://angular.io/guide/router)[**routeur** https://angular.io/guide/router](https://angular.io/guide/router), nous ajoutons donc autant de lien que de endpoint défini dans le fichier `app.router.ts`.
Pour utiliser le [composant](https://angular.io/guide/architecture-components) `HeaderComponent` nous devons à présent utiliser le sélecteur auquel il correspond. Pour identifier ce sélecteur il suffi d'ouvrir le fichier `header.component.ts` pour repérer la valeur de la propriété `selector` du [décorateur](https://medium.com/@madhavmahesh/list-of-all-decorators-available-in-angular-71bdf4ad6976) `@Component` (en général "app-" plus le nom du composant). Il est possible à présent d'uiliser ce sélecteur dans notre application pour afficher afficher notre navigation
Nous ouvrons le fichier `app.component.ts` pour ajouter juste avant la balise `router-outlet` le sélecteur de notre [composant](https://angular.io/guide/architecture-components) `HeaderComponent` :
```typescript=
<app-header></app-header>
```
La gestion des routes de l'application est à présent terminée, nous pouvons tester notre configuration en nous rendant à l'adressee http://localhost:4200/ pour tester notre [composant](https://angular.io/guide/architecture-components) enfant et les routes qui doivent toutes afficher le [composant](https://angular.io/guide/architecture-components) principal associé.
---
<br><br><br><br><br><br>
# Requêtes HTTP, CRUD et service Angular
*Data ? Vous avez dit data ?*

## Création d'un service "Injectable"
La gestion des [services](https://angular.io/guide/architecture-services) est l'un des aspects principaux à bien comprendre en [Angular](https://angular.io) dans la mesure où une application Web est alimentée par des données [asynchrones](https://fr.wikipedia.org/wiki/Ajax_(informatique)), il est primordiale de bien comprendre le principe de [Single page application](https://en.wikipedia.org/wiki/Single-page_application) et celui de [Promise](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise) pour aborder celle plus spécifique des [services](https://angular.io/guide/architecture-services) [Angular](https://angular.io).
Les [services](https://angular.io/guide/architecture-services) [Angular](https://angular.io) peuvent être séparés en deux catégories distinctes : les [Injectables](https://angular.io/api/core/Injectable) et les [Observables](https://rxjs-dev.firebaseapp.com/guide/observable). Dans notre démonstration nous les utilisons soit pour récupérer ou envoyer des informations via le protocole [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) soit pour distribuer cette information dans notre application. Nous commançons par mettre en place un service [Injectables](https://angular.io/api/core/Injectable) car c'est lui qui nous permettra d'exécuter des requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) dans notre application.
Pour commencer nous devons importer un module spécifique et le définir dans le fichier `app.module.ts` que nous ouvrons pour importer le module [HttpClientModule](https://angular.io/guide/http) :
```typescript=
import { HttpClientModule } from "@angular/common/http";
```
Comme pour tous les modules [Angular](https://angular.io) nous devons ajouter [HttpClientModule](https://angular.io/guide/http) dans le tableau des imports du décorateur `@NgModule` afin de rendre disponible les requêtes HTTP :
```typescript=
imports: [
...
HttpClientModule
]
```
Nous pouvons à présent créer notre [service](https://angular.io/guide/architecture-services) [Angular](https://angular.io) qui permet d'organiser les requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) dans notre application de manière à la structurer un maximum. Nous allons dans un premier temps créer un dossier `services` à la racine de notre dossier `app` :
```
mkdir src/app/services
```
Une fois ce dossier créé nous pouvons y ajouter notre premier [service](https://angular.io/guide/architecture-services) avec la commande suivantes :
```
ng g s services/crud/crud
```
Nous avons à présent un fichier `crud.service.ts` placé dans le dossier `services > crud`, nous pouvons l'ouvrir ce fichier pour commencer la configuration du service avec le code suivant :
```typescript=
// Angular
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
//
/*
Definition
*/
@Injectable()
export class CrudService {
// Inject module(s) in the service
constructor( private HttpClient: HttpClient ){};
/*
Methods to get API responses
*/
// Get the API response
private getData = (apiResponse: any) => apiResponse || {};
// Get the API error
private handleError = (apiError: any) => Promise.reject(apiError.error);
//
};
//
```
Dans un premier temps nous importons la class `HttpClient` dans le fichier pour pouvoir utiliser les requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) dans le [service](https://angular.io/guide/architecture-services) et nous injecton la class `HttpClient` en créant une propriété privée nommée également `HttpClient` dans le constructeur. Nous créons ensuite deux méthodes dans le service qui nous permettrons d'analyser le retour des requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol).
Nous pouvons à présent nous intéresser au principe du [CRUD](https://fr.wikipedia.org/wiki/CRUD) dans notre [service](https://angular.io/guide/architecture-services) en ajoutant des méthodes qui nous permettront d'exécuter les différents types de requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol). Les premières méthodes que nous allons créer sont celles qui permettent d'exécuter des requêtes de type `GET` vers une URL. Nous nous plaçon en dessous du constructeur et nous ajoutons le code suivant :
```typescript=
// CRUD method: read item
public readOneItem(param: String): Promise<any>{
return this.HttpClient.get(`https://jsonplaceholder.typicode.com/posts?${param}`)
.toPromise().then(this.getData).catch(this.handleError);
};
// CRUD method: read all items
public readAllItems(): Promise<any>{
return this.HttpClient.get(`https://jsonplaceholder.typicode.com/posts/`)
.toPromise().then(this.getData).catch(this.handleError);
};
```
Dans ces deux méthodes nous utilisons la fonction `get()` de la propriété `HttpClient` que nous avons créé dans le constructeur. La différrence notable entre ces deux méthodes est le fait d'intégrer ou non un paramètre `param` en fin d'URL. Nous pouvons à présent passer à la suite des méthodes du [CRUD](https://fr.wikipedia.org/wiki/CRUD) mais nous devons tout d'abord intégrer un nouveau module dans le [service](https://angular.io/guide/architecture-services) en modifiant un de nos imports de cette façon :
```typescript=
import { HttpClient, HttpHeaders } from '@angular/common/http';
```
Nous ajoutons la class `HttpHeaders` car pour les requêtes de type `POST` par exemple ne devons définir la méthode de communication que nous allons utiliser dans nos requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol). Dans la plupart des cas le format de donnée [JSON](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation) est utilisé dans les échanges d'informations en [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) mais il est possible d'envoyer un grand nombre de format de données différents.
Nous nous plaçon en-dessous des dernières méthodes du [CRUD](https://fr.wikipedia.org/wiki/CRUD) que nous avons créé pour ajouter une méthode qui permet d'exécuter des requêtes de type `POST` vers une URL :
```typescript=
// CRUD method: create item
public createItem(data: any): Promise<any>{
// Set header
let myHeader = new HttpHeaders();
myHeader.append('Content-Type', 'application/json');
// Launch request
return this.HttpClient.post(`https://jsonplaceholder.typicode.com/posts`, data, { headers: myHeader })
.toPromise().then(this.getData).catch(this.handleError);
};
```
Nous utilisons dans cette nouvelle méthode la méthode `post()` de la propriété `HttpClient`, cette dernière nécessite en paramêtre en plus de l'URL les données à envoyer ainsi que le header que nous avons configuré dans la variable `myHeader`.
Après la création de c'est trois méthodes il est à constaté que la propriété `HttpClient` contient différentes méthodes qui correspondent aux types de requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) que nous utilisons dans notre [CRUD](https://fr.wikipedia.org/wiki/CRUD). Nous en déduisons donc que les méthodes `update` et `delete` exitent également, ce qui nous permet de créer les deux dernières méthodes qui nous permettent d'exécuter des requêtes de type `PUT` et `DELETE` vers une URL :
```typescript=
// CRUD method: edit an item
public updateItem(_id: String, data: any): Promise<any>{
// Set header
let myHeader = new HttpHeaders();
myHeader.append('Content-Type', 'application/json');
// Launch request
return this.HttpClient.put(`https://jsonplaceholder.typicode.com/posts/${_id}`, data, { headers: myHeader })
.toPromise().then(this.getData).catch(this.handleError);
};
// CRUD method: delete an item
public deleteItem(_id: String): Promise<any>{
// Set header
let myHeader = new HttpHeaders();
myHeader.append('Content-Type', 'application/json');
// Launch request
return this.HttpClient.delete(`https://jsonplaceholder.typicode.com/posts/${_id}`, { headers: myHeader })
.toPromise().then(this.getData).catch(this.handleError);
};
```
Nous conservons la même logique d'utilisation de paramêtre dans nos méthodes pour identifier précisément la donné à atteindre et dans le cadre des requête de type `PUT` pour envoyer des donnés vers l'URL.
Toutes les méthodes de notre [service](https://angular.io/guide/architecture-services) étant mises en place, nous devons à présent éditer le fichier `app.module.ts` car, à la manière de notre module de [routing](https://angular.io/guide/router), nous devons importer notre [service](https://angular.io/guide/architecture-services) pour qu'il soit disponible dans notre application. Nous ouvrons donc le fichier `app.module.ts` pour importer le service de cette manière :
```typescript=
import { CrudService } from "./services/crud/crud.service";
```
Puis nous ajoutons notre [service](https://angular.io/guide/architecture-services) dans le [décorateur](https://medium.com/@madhavmahesh/list-of-all-decorators-available-in-angular-71bdf4ad6976) `@NgModule` au niveau du tableau des providers :
```typescript=
@NgModule({
...
providers: [CrudService],
...
})
```
<br>
## Création d'un service "Observable"
Nous allons à présent aborder l'utilisation des [Observables](https://rxjs-dev.firebaseapp.com/guide/observable) dans un service [Angular](https://angular.io) qui peuvent être comparrés aux notions de STORE dans la mesure ou les [Observables](https://rxjs-dev.firebaseapp.com/guide/observable) permettent de distribuer l'information dans une application.
Il faut considérer les [Observables](https://rxjs-dev.firebaseapp.com/guide/observable) comme des sujets auxquels un [composant](https://angular.io/guide/architecture-components) peut s'abonner (*subscribe*) ou publier (*publish*), chaque [composant](https://angular.io/guide/architecture-components) abonné à un sujet est mit à jour automatiquement lorsque qu'un autre [composant](https://angular.io/guide/architecture-components) publie une mise à jour du sujet. Nous allons donc à présent créer un nouveau [service](https://angular.io/guide/architecture-services) pour mettre en pratique cette logique :
```
ng g s services/observable/observable
```
Nous allons tout d'abord configurer ce nouveaeu [service](https://angular.io/guide/architecture-services) en y important les class `BehaviorSubject` et `Observable` dans le fichier `observable.service.ts` :
```typescript=
/*
Imports
*/
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
//
/*
Definition and export
*/
@Injectable({
providedIn: 'root'
})
export class ObservablesService {
constructor() {}
}
//
```
La structure de ce fichier est similaire au fichier `crud.service.ts` si ce n'est que nous importons deux class suplémentaires pour que nous puission gérer nos [Observables](https://rxjs-dev.firebaseapp.com/guide/observable). Nous allons commencer par définir un sujet car c'est par lui que nos [composant](https://angular.io/guide/architecture-components) pourront partager des informations, nous plaçons sous le constructeur puis nous ajoutons un sujet, c'est à dire une propriété nommée `userInfo` :
```typescript=
protected userInfo: BehaviorSubject<any> = new BehaviorSubject<any>(null);
```
La propriété `userInfo` est protégée, c'est à dire qu'il est impossible d'en connaître la valeur hors du [service](https://angular.io/guide/architecture-services) `ObservablesService`, c'est pourquoi il faut à présent ajouter une méthode qui permettra aux composants qui s'abonnent à cette information d'en récupérer la valeur :
```typescript=
public getUserInfo(): Observable<any> { return this.userInfo };
```
Nous allons à présent permettre à des [composants](https://angular.io/guide/architecture-components) de mettre à jour la valeur de `userInfo`, dans la mesure ou la propriété est protégée, nous devons ajouter la fonction suivante :
```typescript=
public setObservableData = (type: string, data: any) => {
switch(type){
case 'user':
this.userInfo.next(data);
break;
default:
break;
};
};
```
> La fonction `next()` permet de mettre à jour la valeur de `userInfo`.
Cette méthode prend deux paramêtre, `type` nous permettra d'utiliser cette méthode pour plusieurs [Observables](https://rxjs-dev.firebaseapp.com/guide/observable) et `data` qui permettra de mettre à les propriétés protégées de notre [service](https://angular.io/guide/architecture-services). Nous voulons que deux sujets soient défini dans notre applications pour permettre de les partager dans nos [composants](https://angular.io/guide/architecture-components), c'est pourquoi nous ajoutons le code nécessaire pour définir les deux sujets suivants :
- userInfo pour partager les informations d'un utilisateur connecté
- postList Pour partager une liste d'articles
Notre [service](https://angular.io/guide/architecture-services) est à présent configuré correctement mais nous devons, comme pour le service [Injectable](https://angular.io/api/core/Injectable) l'importer dans le fichier `app.module.ts` pour ajouter la class `ObservablesService` dans le tableau des providers afin qu'il soit disponible dans notre application.
<br>
## Connecter le service Injectable au service Observable
Notre application dispose à présents de deux [services](https://angular.io/guide/architecture-services) différent, un [Injectable](https://angular.io/api/core/Injectable) qui permet d'éxécuter des requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) et un [Observable](https://rxjs-dev.firebaseapp.com/guide/observable) qui permet de partageer des information dans notre application mais comment faire communiquer les deux ? Nous allons tout d'abord faire un sorte qu'une valeur puisse être mise à jour en partant du principe suivant : lorsqu'un utilisateur se connect toutes ses informations sont partagées dans l'application.
Nous ouvrons donc à présent le fichier `crud.service.ts`, nous allons ajouter un paramêtre `endpoint` aux métodes `readOneItem()` et `getData()` qui nous permettra d'ajouter un `switch` dans la méthode `getData()` pour mettre à jour la bonne information dans le service [Observable](https://rxjs-dev.firebaseapp.com/guide/observable).
Nous devons tout d'abord importer notre class `ObservablesService` pour l'injecter dans le constructeur de la class `CrudService` :
```typescript=
import { ObservablesService } from "../observable/observable.service";
@Injectable()
export class CrudService {
constructor(
private HttpClient: HttpClient,
private ObservablesService: ObservablesService
){};
...
}
```
Comme pour la class `HttpClient` nous crééons une prépriété privée nommé `ObservablesService` puis nous mettons à jour les fonctions `readOneItem()` et `getData()` :
```typescript=
public readOneItem(endpoint: String, param: String): Promise<any>{
return this.HttpClient.get(`https://jsonplaceholder.typicode.com/${endpoint}?${param}`).toPromise()
.then( data => this.getData(endpoint, data))
.catch(this.handleError);
};
private getData = (endpoint, apiResponse: any) => {
// Switch endpoint to set observable value
switch(endpoint){
case 'users':
// Set user info obserrbale value
this.ObservablesService.setObservableData('user',apiResponse)
// Return data
return apiResponse || {};
break;
default:
// Retun data anytime
return apiResponse || {};
break;
};
};
```
Les modifications que nous venons d'apporter sur le fichire `crud.service.ts` au niveau de la méthode `getData()` nécessite la mise à jour des autres méthodes du [CRUD](https://fr.wikipedia.org/wiki/CRUD) pour y intégrer la paramêtre `endpoint` et un `case` dans la méthode `getData()` pour pourvoir mettre à jour la valeur de la propriété `postList` pour finaliser la mise à jour du fichier `crud.service.ts`.
<br>
## Utiliser un service Injectable dans un composant
Il est possible d'utiliser un ou plusieurs [services](https://angular.io/guide/architecture-services) dans un [composant](https://angular.io/guide/architecture-components) principal comme dans un [composant](https://angular.io/guide/architecture-components) enfant, dans le cadre de notre projet nous allons préférer utiliser le [service](https://angular.io/guide/architecture-services) `CrudService` dans nos [composants](https://angular.io/guide/architecture-components) principaux car cela permet de bien structurer les échanges d'informations.
Les étapes à suivre reste les mêmes pour intégrer des [services](https://angular.io/guide/architecture-services), nous prenons en exemple le fichier `app.component.ts` dans lequel nous importons et le [service](https://angular.io/guide/architecture-services) `CrudService` :
```typescript=
import { CrudService } from "./services/crud/crud.service";
```
Puis nous ajoutons une propriété privée dans le constructeur :
```typescript=
constructor(
private CrudService: CrudService
){}
```
Le fichier `home-page.component.ts` constitue le point d'entré de notre application, c'est pourquoi nous allons y ajouter une méthode qui permet de tester une adressse email pour savoir si elle fait partie de la liste des utilisateurs. Nous nous plaçon souus le constructeur puis nous ajoutons le code suivant :
```typescript=
private getUserInfo = (email: String ) => {
// Use CrudService to get user infos
this.CrudService.readOneItem('users', `email=${email}`)
.then( data => {
console.log('SUCCES request', data);
})
.catch( error => {
console.log('ERROR request', error);
});
};
```
Puis, pour lancer cette méthode dès le chargement du [composant](https://angular.io/guide/architecture-components) `HomePageComponent` nous éditons la méthode `ngOnInit()` :
```typescript=
ngOnInit(){
this.getUserInfo('Sincere@april.biz');
};
```
> Nous mettons en paramêtre une adresse email valide par default afin de tester la méthode `getUserInfo()`.
Avec l'intégration du [service](https://angular.io/guide/architecture-services) `CrudService` et l'utilisation de sa méthode `readOneItem()` dans le fichier `app.component.ts` nous venons de comprendre comment utiliser les méthodes d'un [service](https://angular.io/guide/architecture-services) dans un [composant](https://angular.io/guide/architecture-components), ce qui nous sera utile quand nous mettron à jour les [composants](https://angular.io/guide/architecture-components) des différentes routes de notre application.
<br>
## Utiliser un service Observable dans un composant
La méthode `getUserInfo()` que nous avons ajouté dans le fichier `app.component.ts` utilise la méthode `readOneItem()` de notre [service](https://angular.io/guide/architecture-services) `CrudService`, dans la mesure ou nos deux [services](https://angular.io/guide/architecture-services) sont connecté l'un à l'autre cela veut dire que lorsque la méthode `getData()` est appelée avec en paramètre `users` comme `endpoint`, les données reçus sont publiées en tant que nouveau sujet pour l'[observable](https://rxjs-dev.firebaseapp.com/guide/observable) `userInfo`. Le but d'un [observable](https://rxjs-dev.firebaseapp.com/guide/observable) étant de partager des informations entre le [composant](https://angular.io/guide/architecture-components), nous pouvons à présent faire en sorte d'afficher une navigation différente pour un utilisateur connecté ou non.
Nous ouvrons donc le fichier `header.component.ts` pour y importer notre [service](https://angular.io/guide/architecture-services) `ObservablesService` en suivant les mêmes étapes que pour le [service](https://angular.io/guide/architecture-services) `CrudService`. Ensuite nous créons une propriété `userData` puis nous modifions le constructeur pour modifier la valeur de `userData` avec notre [service](https://angular.io/guide/architecture-services) `ObservablesService` :
```typescript=
export class HeaderComponent implements OnInit {
/*
Declaration
*/
// Properties
public userData: any;
constructor(
private ObservablesService: ObservablesService
){
// Get user data observer
this.ObservablesService.getUserInfo().subscribe( userDataObserver => {
if(userDataObserver === null) { this.userData = null }
else{ this.userData = userDataObserver[0] }
})
}
//
ngOnInit(){};
};
```
> La valeur de `userDataObserver` est un tableaeu, c'est pourquoi nous n'en récupérons que le premier index dans ce cas précis.
Il est possible à présent d'utiliser la propriété `userData` dans le fichier `header.component.html` pour ajouter une condition qui affiche le nom de l'utilisateur si la valeur de `userdata` est égale à `null` :
```htmlmixed=
<h1 *ngIf="userData !== null" [innerText]="userData.name"></h1>
```
Nous venons de voir comment mettre à jour une valeur grâce à l'utilissation du [service](https://angular.io/guide/architecture-services) `ObservablesService`, cette technique peut être appliquée pour tous types d'informations dans n'importe quel [composant](https://angular.io/guide/architecture-components) et s'avère très utile pour afficher des informations particulières sans avoir à répéter des requêtes [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) qui renvoie la même information.
---
<br><br><br><br><br><br>
# Les logiques de développement d'une application Web
*Single page application et DOM dynamique*

## Définition d'une application web
Dans le cadre du développement d'une application web peu importe le fait de la développer avec un framework ou non, il est recommandé de suivre une logique qui intègre la notion de [DOM dynamique](https://fr.wikipedia.org/wiki/HTML_dynamique). Une application Web, ou Single page application, s'affiche sur une seule est unique page HTML et c'est pourquoi ces applications sont la plupart du temps connectées à des API qui distribuent l'information qu'elles doivent afficher. Nous utilisons dans notre application l'[API](https://fr.wikipedia.org/wiki/Interface_de_programmation) [JSONplaceHolder](https://jsonplaceholder.typicode.com) mais peu importe l'[API](https://fr.wikipedia.org/wiki/Interface_de_programmation) utilisée, les méthodes de notre [service](https://angular.io/guide/architecture-services) `CrudService` sont les mêmes.
Nous allons donc développer les différentes logiques inhérentes aux applications web dans notre application [Angular](https://angular.io) mais elles peuvent s'adapter à n'importe quel framework ou au développement natif d'une application web.
<br>
## Créer un formulaire de connexion
Nous commençons par créer un élément essentiel à notre application, à savoir le formulaire de connexion. Nous allons intégrer dans le [composant](https://angular.io/guide/architecture-components) `HomePageComponent` un [composant](https://angular.io/guide/architecture-components) enfant qui contiendra notre formulaire. Nous créons donc ce [composant](https://angular.io/guide/architecture-components) avec la commande
```
ng g c shared/formLogin -is
```
Nous allons configurer ce [composant](https://angular.io/guide/architecture-components) pour qu'il puisse gérer un formulaire, nous ouvons le fichier `form-login.component.ts` pour importer les class `FormBuilder`, `FormGroup` et `Validators` :
```typescript=
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
```
Puis nous ajoutons dans la class `FormLoginComponent` le code qui nous permet d'initialiser le formulaire de connexion :
```typescript=
export class FormLoginComponent implements OnInit {
// Declarations
public formData: FormGroup;
// Inject FormBuilder
constructor(
private FormBuilder: FormBuilder
) {}
// Method to reset form
private resetForm = () => {
this.formData = this.FormBuilder.group({
email: [ null, Validators.required ]
});
};
// Start
ngOnInit() {
this.resetForm();
}
};
```
La propriété `formData` est typée avec la class `FormGroup` ce qui permet de l'utiliser pour définir des données requises pour valider un fomualaire. Nous allons à présent éditer la vue HTML du composant `FormLoginComponent` en ouvrant le fichier `form-login.component.html` et en y ajoutant le code suivant :
```htmlmixed=
<form
[formGroup]="formData"
>
<input
formControlName="email"
type="email" name="email" required minlength="5" placeholder="Your email"
>
<button
[disabled]="!formData.valid"
type="submit"
>OK</button>
</form>
```
Dans cee formulaire nous utilisons différrentes directive issues de la class `FormGroup` qui permettent de dynamiquement récupérer les informations du formulaire et d'activer ou non le bouton `submit` selon que le formulaire soit rempli ou non.
Nous allons à présent aborder la soumission du formulaire car nous devons faire en sorte que les données du formulaire puisse être envoyées à la méthode `getUserInfo` du fichier `app.component.ts`. Nous important donc les class `EventEmitter` dans le fichier `form-login.component.ts` :
```typescript=
import { Component, OnInit, EventEmitter } from '@angular/core';
```
Puis nous crééons une nouvelle propriété `formSubmit` qui est un événement généré avec la class `EventEmitter` :
```typescript=
@Output() formSubmit = new EventEmitter();
```
Cette événement `formSubmit` sera utile pour transférer les informations du formulaire du composant `LoginFormComponent` vers le composant `AppComponnent`. Nous allons donc émettre l'événement `formSubmit` lors du submit du formulaire :
```htmlmixed=
<form
[formGroup]="formData"
(submit)="formSubmit.emit(formData.value.email)"
>
...
```
Notre formulaire de connexion eest à présent terminé mais il nous reste une dernière modification dans le fichier `app.module.ts` car pour que toute les fonctionnalités des formulaire soient disponibles dans l'application nous devons importer les class `FormsModule` et `ReactiveFormsModule` :
```typescript=
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
```
Sans oublier de les ajouter dans le tableau des imports :
```typescript=
imports: [
...
FormsModule,
ReactiveFormsModule
],
```
Nous pouvons à présent terminer l'implémentation de notre formulaire de connexion en modifiant le fichier `home-page.component.html` pour qu'il intégre le formulaire :
```htmlmixed=
<app-form-login
(formSubmit)="getUserInfo($event)"
></app-form-login>
```
A cette étape nous n'avons plus besoin d'appeler la méthode `getUserInfo()` dans le composant `HomePageComponent` et formulaire permet de mettre à jour la valeur de la propriété `userInfo` du fichier `observable.service.ts`.
<br>
## Connecter un utilisateur
La connexion sécurisée d'un utilisateur intègre nécessairement des logiques serveur pour être efficace, le principe générélament utilisé est de générer un token utilisateur stocké dans un cookie. Nous allons utiliser dans notrre applicatioon une notion plus simple qui consite à enregistrer l'email d'un utilisateur en localstorage mais cette solution n'est absolument pas acceptable en production.
Nous allons donc éditer le fichier `header.component.ts` pour stocker l'email de l'utilisateur au moment ou il se connect :
```typescript=
this.ObservablesService.getUserInfo().subscribe( userDataObserver => {
if(userDataObserver === null) { this.userData = null }
else{
if(userDataObserver.length > 0){
// Set local storage
localStorage.setItem('userEmail', userDataObserver[0].email );
// Update userData value
this.userData = userDataObserver[0];
}
else{
this.userData = null
}
}
})
```
> Nous ajoutons une condition qui vérifie si `userDataObserver` contient au minimum une donnée.
Nous allons à présent éditer la méthode `ngOnInit()` du fichier `app.component.ts` pour tester l'email stocké en localstorage dès le chargement de l'application :
```typescript=
/*
Import
*/
// Angular
import { Component, OnInit } from '@angular/core';
// Inner
import { CrudService } from "./services/crud/crud.service";
//
/*
Componant configuration
*/
@Component({
selector: 'app-root',
template: `
<app-header></app-header>
<router-outlet></router-outlet>
`
})
//
/*
Componant class definition
*/
export class AppComponent implements OnInit {
constructor(
private CrudService: CrudService
){}
async ngOnInit(){
await this.CrudService.readOneItem('users', `email=${localStorage.getItem('userEmail')}`);
};
};
//
```
<br>
## Déconnecter un utilisateur
Pour déconnecter un utilisateur de notre application il faut supprimer l'email du localstorage et mettre à jour la valeur de la propriété `userInfo` de la class `ObservableService`. Nous ajoutons tout d'abord un nouveau lien dans le fichier `header.component.html` :
```htmlmixed=
<a href="#" (click)="logout()">Logout</a>
```
Ce nouveau lien déclenche au click une fonction `logout()` que nous devons à pésent créer dans le fichier `header.component.html`. Nous nous après le constructeur eet nous ajoutons le code suivant :
```typescript=
public logout = () => {
// Delete localstorage
localStorage.removeItem('userEmail');
// Set user info obserrbale value
this.ObservablesService.setObservableData('users', null)
}
```
## Réserver des routes aux utilisateurs connectés
Au delà d'afficher ou non des élements dans le [DOM](https://fr.wikipedia.org/wiki/Document_Object_Model) selon les valeurs d'une propriété, il est important de configurer notre module de [routing](https://angular.io/guide/router) pour qu'il puisse empécher l'accès à certaines routes selon des critères spécifiques. Nous allons dans un premier temps mettre à jour notre fichier `header.component.html` pour gérer l'affichage dynamique des élements de la navigation :
```htmlmixed=
<nav>
<ul>
<li *ngIf="!userData">
<a [routerLink]="'/'">Home</a>
</li>
<li *ngIf="userData">
<a [routerLink]="'/connected'">Connected</a>
</li>
<li *ngIf="userData">
<a href="#" (click)="logout()">Logout</a>
</li>
</ul>
</nav>
```
Notre navigationn affiche à présent le lien `home` pour les utilisateurs non-connectés et les liens `Connected` et `Logout` pour les utilisateur connectés. Nous devons ajouter à présent un fichier à la racine de notre dossier `app` pour gérer les régles qui seront utilisées pour rendre accessible ou non une route :
```
touch src/app/auth.guard.ts
```
Nous ouvrons à présent ce fichier et collons le code suivant :
```typescript=
// Imports
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
// Inner
import { CrudService } from "./services/crud/crud.service";
// Definition
@Injectable({ providedIn: 'root' })
// Export
export class AuthGuard implements CanActivate {
constructor(
private CrudService: CrudService,
private Router: Router,
){}
canActivate(): Promise<any> {
return new Promise( (resolve, reject) => {
this.CrudService.readOneItem('users', `email=${localStorage.getItem('userEmail')}`)
.then( ( apiResponse ) => {
if(apiResponse.length > 0){ return resolve(true) }
else{ this.Router.navigateByUrl('/') };
})
.catch( ( apiResponse ) => this.Router.navigateByUrl('/'))
})
}
}
```
La méthode `canActivate()` renvoi une [promesse](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise) qui fait à la méhode `readOneItem()` de la class `CrudService`, selon les informations obtenues la [promesse](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise) est résolue pour rendre une route accesssible ou rediriger l'utilisateur vers la page d'accueil.
Pour mettre à jour le module de [routing](https://angular.io/guide/router) devons importer dans le fichier `app.routing.ts` la class `AuthGuard` afin d'utiliser la méthode `canActivate()` dans les routes que nous souhaitons protéger :
```typescript=
import { AuthGuard } from "./auth.guard";
```
Ensuite nous éditons la constante `AppRouterModule` pour ajouter dans les routes `connected` et `crud` la propriété suivante :
```typescript=
canActivate: [ AuthGuard ]
```
Toutes les routes de notre application sont à présent configurés et seuls les utilisateurs connectés auront accès à des routes spécifiques.
<br>
## Rediriger un utilisateur
Dernière étapes de la gestion des routes protégées, nous allons à présent éditer le fichier `app.component.ts` pour pouvour rediriger automatiquement les utilisateurs connectés vers la route `connected`. Pour ce faire nous devons dans un premier temps importer la class `Router` dans le fichier :
```typescript=
import { Router } from '@angular/router';
```
Pour ensuite modifier la méthode `ngOnInt()` de cette façon :
```typescript=
async ngOnInit(){
const userInfo = await this.CrudService.readOneItem('users', `email=${localStorage.getItem('userEmail')}`);
// Check user info
if(userInfo.length > 0){
// Change route endpoint
this.Router.navigateByUrl('/connected');
};
};
```
Nous allons importer également la class `Router` dans le fichier `home-page.component.ts` pour y modifier la la méthode `getUserInfo()` de cette façon :
```typescript=
public getUserInfo = async (email: String ) => {
// Get user infos
const userInfo = await this.CrudService.readOneItem('users', `email=${email}`);
// Check user info
if(userInfo.length > 0){
// Change route endpoint
this.Router.navigateByUrl('/connected');
}
};
```
<br>
## Injecter des informations dans un composant enfant
Nous allons à présent créer un [composant](https://angular.io/guide/architecture-components) enfant que nous utiliserons dans le [composant](https://angular.io/guide/architecture-components) `ConnectedPageComponent` pour afficher une liste d'articles dans la route. Mais dans un premier temps, nous allons ajouter dans le [composant](https://angular.io/guide/architecture-components) `ConnectedPageComponent` une méthode pour charger la liste des articles. Nous ouvrons donc le fichier `connected-page.component.ts` pour importer le service `CrudService`, déclarer une propriété `postCollection` et ajouter la méthode `getPostList()` :
```typescript=
/*
Import
*/
// Angular
import { Component, OnInit } from '@angular/core';
// Inner
import { CrudService } from "../../services/crud/crud.service";
//
/*
Componant configuration
*/
@Component({
selector: 'app-crud-page',
templateUrl: './crud-page.component.html',
})
//
/*
Componant class definition
*/
export class CrudPageComponent implements OnInit {
/*
Declarations
*/
public postCollection: any;
constructor(
private CrudService: CrudService
){}
//
/*
Methods
*/
// Method to get the post list
public getPostList = async () => {
this.postCollection = await this.CrudService.readAllItems('posts');
};
//
/*
Hooks
*/
ngOnInit(){
// Get the poost list
this.getPostList();
};
//
};
//
```
Nous appelons la méthode `getPostList()` dans la méthode `ngOnInit()` afin de charger les informations dès le chargement du [composant](https://angular.io/guide/architecture-components). Nous pouvons à présent créer le [composant](https://angular.io/guide/architecture-components) pour afficher les articles avec la commande suivante :
```
ng g c shared/itemPost -is
```
Pour importer des informations depuis un [composant](https://angular.io/guide/architecture-components) parent il faut utiliser le [décorateur](https://medium.com/@madhavmahesh/list-of-all-decorators-available-in-angular-71bdf4ad6976) `@Input()`, nous ouvrons donc le fichier `item-post.component.ts` pour y coller le code suivant :
```typescript=
/*
Import
*/
// Angular
import { Component, OnInit, Input } from '@angular/core';
//
/*
Componant configuration
*/
@Component({
selector: 'app-item-post',
templateUrl: './item-post.component.html'
})
//
/*
Componant class definition
*/
export class ItemPostComponent implements OnInit {
// Input data from parent component
@Input() post: any;
constructor(){}
ngOnInit(){};
};
//
```
Puis nous ouvrons le fichier `item-post.component.html` pour ajouter les balises HTML suivantes :
```htmlmixed=
<h2 [innerText]="post.title"></h2>
<p [innerText]="post.body"></p>
```
Pour finir, nous ouvrons le fichier `connected-page.component.html` pour utiliser le [composant](https://angular.io/guide/architecture-components) `ItemPostComponent` dans une boucle en lui injectant les informations relatives à un item de la propriété `postCollection` :
```htmlmixed=
<article *ngFor="let item of postCollection">
<app-item-post
[post]="item"
></app-item-post>
</article>
```
A la fin de cette étape la liste des articles s'affiche dans la route `/connected` grâce à un [composant](https://angular.io/guide/architecture-components) qui est alimenté par les donnnées issues d'une requête [HTTP](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol) déclenchée dans le [composant](https://angular.io/guide/architecture-components) dont il est l'enfant.
---
<br><br><br><br><br><br>
# Ressources

## Liens utiles
La liste ci-dessous contient les liens utiles cité dans ce document.
- [**Angular** https://angular.io](https://angular.io)
- [**TypeScript** https://www.typescriptlang.org/](https://www.typescriptlang.org/)
- [**CLI** https://fr.wikipedia.org/wiki/Interface_en_ligne_de_commande](https://fr.wikipedia.org/wiki/Interface_en_ligne_de_commande)
- [**Angular CLI** https://cli.angular.io/](https://cli.angular.io/)
- [**Component** https://angular.io/guide/architecture-components](https://angular.io/guide/architecture-components)
- [**Routeur** https://angular.io/guide/router](https://angular.io/guide/router)
- [**HTTP** https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol)
- [**Endpoints** https://en.wikipedia.org/wiki/Web_API](https://en.wikipedia.org/wiki/Web_API)
- [**DOM** https://fr.wikipedia.org/wiki/Document_Object_Model](https://fr.wikipedia.org/wiki/Document_Object_Model)
- [**Directive routerLink** https://angular.io/api/router/RouterLink](https://angular.io/api/router/RouterLink)
- [**Angular décorator** https://medium.com/@madhavmahesh/list-of-all-decorators-available-in-angular-71bdf4ad6976](https://medium.com/@madhavmahesh/list-of-all-decorators-available-in-angular-71bdf4ad6976)
- [**Angular services** https://angular.io/guide/architecture-services](https://angular.io/guide/architecture-services)
- [**Web asynchrones** https://fr.wikipedia.org/wiki/Ajax_(informatique)](https://fr.wikipedia.org/wiki/Ajax_(informatique))
- [**Single page application** https://en.wikipedia.org/wiki/Single-page_application](https://en.wikipedia.org/wiki/Single-page_application)
- [**Promise** https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise)
- [**Injectables** https://angular.io/api/core/Injectable](https://angular.io/api/core/Injectable)
- [**Observables** https://rxjs-dev.firebaseapp.com/guide/observable](https://rxjs-dev.firebaseapp.com/guide/observable)
- [**HttpClientModule** https://angular.io/guide/http](https://angular.io/guide/http)
- [**CRUD** https://fr.wikipedia.org/wiki/CRUD](https://fr.wikipedia.org/wiki/CRUD)
- [**JSON** https://fr.wikipedia.org/wiki/JavaScript_Object_Notation](https://fr.wikipedia.org/wiki/JavaScript_Object_Notation)
- [**DOM dynamique** https://fr.wikipedia.org/wiki/HTML_dynamique](https://fr.wikipedia.org/wiki/HTML_dynamique)
- [**API** https://fr.wikipedia.org/wiki/Interface_de_programmation](https://fr.wikipedia.org/wiki/Interface_de_programmation)