Angular X

Formateur
29ans
Formateur Angular
Architecte technique






Tour de table
Noms prénoms
Parcours
Division et site
Expérience en dev. et JS/Angular
Vos attentes
Déroulement de la formation
Déroulement de la formation
Installation des outils
> npm install -g typescript
> npm install -g @angular/cliGit
NodeJs
Un IDE
Depuis l'invite de commandes




> npm uninstall -g @angular/cli
> npm cache clean
> npm install -g @angular/cliOU
Répertoire d'installation en local : M:\Env formation Angular
Déroulement de la formation
Github de la formation
> git clone -b step-00 https://github.com/Jmadih/angularX
> cd angularX
> npm install
> ng serve
●une branche d’exercice : step-00
●une branche de solution : step-00-solution
Un concept => une branche
Avant d'aller plus loin
-
Javascript (ES6, ES5)
-
Typescript (ES6+, types, annotation)
-
Dart
Les languages supporter par Angular :
Javascript & Typescript
@Component({
selector: 'my-app',
template: '<h1 id="output">My First Angular 2 App</h1>'
})
class AppComponent { }
var AppComponent = ng.core.Component({
selector: 'my-app',
template: '<h1 id="output">My First Angular 2 App</h1>'
})
.Class({
constructor: function () { }
});
Typescript
Javascript
Javascript & Typescript
Javascript
-
Arrow functions -
Classes -
Collections -
get / set transparents -
let / const -
ES6 modules - ....
Typescript
-
Decorators -
Classes -
Interfaces -
Typings -
Function return -
Optional params - ....
DEMO
Angular Ecosystem

Angular 10


Angular 9

Configuration
ES5 est désactivé par défaut, pour ajouter un navigateur => .browserlistrc
Quickstart

CLI


-
Génère l’arborescence de l’application
-
Initialise un repo Git + 1er commit
-
Installe les deps NPM + Typings
> ng new my-best-appNouveau projet :

> ng g component home
create src/app/home/home.component.html (23 bytes)
create src/app/home/home.component.spec.ts (614 bytes)
create src/app/home/home.component.ts (262 bytes)
create src/app/home/home.component.scss (0 bytes)
update src/app/app.module.ts (390 bytes)
Nouveau composant :

Component : ng g component [name] --flat
Directive : ng g directive [name]
Pipe : ng g pipe [name]
Service : ng g service [name]
Class : ng g class [name]
Interface : ng g interface [name]
Enum : ng g enum [name]
Build : ng build -prod
Lint : ng lint
Directives : ng doc directives
Toutes les commandes :

-
Module
-
Component
-
Template syntax
-
Directive
-
Pipe
Les concepts Angular
-
Router
-
Service Provider
-
Forms
-
HTTP
-
Observable
(Advanced: Angular elements, Ivy, Schematics ...)
Architecture globale
Une application est un arbre de modules et de composants
Module

Arbre de module
Déclaration d'un module
Une classe avec un décorateur @NgModule
Paramètres : declarations, imports, exports, providers, bootstrap.
Charger l’application dans la page
Bootstrapping
Component


3 concepts à retenir
Déclaration d'un composant
Composant
Template
Lifecycle Hooks


Lifecycle Hooks
Déclaration
Exercice
1
git checkout -f step-01
-
Créez un composant Home (HomeComponent)
-
Intégrez le dans le template du composant parent (Ici AppComponent) -
Créez un module Home (HomeModule)
-
Déclarez le composant Home dans le module Home
Solution
1
git checkout -f step-01-solution
Des questions ?
Template

Chaque composant a son
template
Un template
Un template :
3 catégories de bindings
Un template :
3 catégories de bindings
<!-- Interpolation -->
<h1>{{introduction.firstname}}</h1>
<!-- One way binding -->
<h1 [innerText]="introduction.firstname"></h1>
<span [style.backgroundColor]="introduction.favouriteColor"></span>
<!-- Two way binding -->
<input [(ngModel)]="introduction.firstname"/>
<!-- Event binding -->
<button (click)="sendForm()">Send</button>Exemples
Aperçu
attributs vs propriétés
Les attributs c'est du HTML, les propriétés c'est du DOM
Les attributs servent à initialiser les propriétés mais pas vice versa !
- Exceptions :
- Propriété sans attribut : (textContent)
- Attribut sans propriété : (colspan)
Aperçu
attributs vs propriétés
<input type="text" value="foo"> <!-- attributs value et type -->
Le navigateur interprète cette balise et crée un objet node en mémoire :
input:
...
value: "foo" // <-- propriétés value et type
type: "text"
...Exercice
2
git checkout -f step-02
-
Dans le Home Component : Remplissez un model de type Introduction
-
Affichez le model dans le template du Home Component
-
Ajoutez un événement qui déclenche"alert('edit')"sur le bouton Edit -
Ajoutez la propriété disable sur le bouton Edit, cette propriété s'active quand on clique sur le bouton
Rendu
Exercice
2

Solution
2
git checkout -f step-02-solution
Des questions ?
Directives

Directives
3 types :
- Structurel
- Attribut
- Composant
Directives structurels
Modifie le DOM en ajoutant ou en supprimant des éléments.
<!-- *ngIf -->
<div *ngIf="hero; else notHero" class="name">{{hero.name}}</div>
<ng-template #notHero >
I'm not a Hero bro
</ng-template>
<!-- *ngFor -->
<ul>
<li *ngFor="let hero of heroes">{{hero.name}}</li>
</ul>
<!-- *ngSwitch -->
<div [ngSwitch]="hero?.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'app-confused'" [hero]="hero"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="hero"></app-unknown-hero>
</div>Directives d'attributs
Change l'apparence ou le comportement d'un élement
<div [style.width.px]="size"></div>
<td [attr.colspan]="5*5">cell</td>
<div [class.isSpecial]="isSpecial">Special class</div>
<div [style.color]="isSpecial ? 'red' : 'green'">Special class</div>
Directives d'attributs
CUSTUM
import {Directive, ElementRef, Input, OnInit, Renderer2} from '@angular/core';
@Directive({
selector: '[custumDirective]'
})
export class custumDirective implements OnInit {
constructor(private el: ElementRef, private renderer: Renderer2) {
}
ngOnInit() {
this.renderer.addClass(this.el.nativeElement, 'wild');
}
}
Directives de composant
Juste une directive avec un template
<app-component>
Hello world...
</app-component>Exercice
3
git checkout -f step-03
-
Affichez la liste des liens introduction.links avec la balise image et
la directive mat-card-avatar -
Affichez selon le status (introduction.status) la bonne icône.
-
Ajoutez les couleurs rouge et vert sur les icônes
-
Ajoutez une custum directive (yearsOfExperience)
-
Appliquez cette directive dans une div après la date de naissance

Rendu
Exercice
3
Solution
3
git checkout -f step-03-solution
Des questions ?
Router

Les composants sont
routables

Navigation
http://localhost:4200/home
http://localhost:4200/skills
Configuration
-
path : l’URL de route (ex: /skills/:id)
-
component : le composant associé à cette route (ex: HomeComponent)
-
redirectTo : le fragment d'URL vers lequel rediriger la route courante (ex : '/home')
-
pathMatch : stratégie de redirection (full/ prefix)
-
full : Reconnaissance depuis la racine de la route
-
prefix : Reconnaissance partielle de la route
-
Simple
Configuration
Complète
-
path
-
component
-
redirectTo
-
pathMatch
- data
- canActivate / canDeactivate
- resolver
- children
Routes
import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {AboutComponent} from "./about/about.component";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills', component: SkillsComponent },
{path : 'about', component : AboutComponent},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});Déclaration
Routes
<!-- app.component.html -->
<router-outlet></router-outlet>Déclaration de l’emplacement dans lequel le composant doit s’afficher
Routes
<button mat-raised-button color="primary" [routerLink]="['/page1']">Page1</button>
<button mat-raised-button color="primary" [routerLink]="['/page2']">Page2</button>
<button mat-raised-button color="primary" [routerLink]="['/page3']">Page3</button>Redirection
Routes
import {ModuleWithProviders} from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IntroductionComponent} from "./introduction/introduction.component";
import {SkillsComponent} from "./skills/skills.component";
import {SkillsResolve} from "./skills/skills.resolve";
import {AboutComponent} from "./about/about.component";
const routes: Routes = [
{path: '', redirectTo: 'introduction', pathMatch: 'full'},
{path : 'introduction', component: IntroductionComponent},
{path : 'skills', component: SkillsComponent, resolve: {skills: SkillsResolve} },
{path : 'about', component : AboutComponent},
{path : '**', component: IntroductionComponent}
];
export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes, {useHash: true});Utilisation d'un resolver
Routes
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
@Injectable()
export class SkillsResolver implements Resolve<any> {
constructor() {}
resolve(activatedRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
const elem: number = activatedRoute.params['param'];
}
}
Déclaration d'un resolver
Routes
@Component({
selector: 'app-my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss']
})
export class MyComponent implements OnInit {
infos: any;
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
this.route.data.subscribe(data => {
this.infos= data['skills'];
});
}Récupérer les données du resolver
Routes
export const appRoutes: Routes = [
{path: 'info/:id', component: InfoComponent, resolve: { info: InfoResolverService}},
{path: 'about', component: AboutComponent, canActivate: [MyGuard]},
{path: '**', component: NotFoundComponent},
{path: '404', component: NotFoundComponent}
];
Utilisation des Guards
Routes
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class ConnectedGuardGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
Déclaration d'un Guard
Exercice
4
git checkout -f step-04
-
Créez un fichier "app.routes.ts" puis ajoutez les deux routes home et skills -
Ajoutez le routerOutlet dans le app.component.html
-
Ajoutez dans le menubar les deux routerLink (home et skills) -
Ajoutez le routerLink dans le fichier not-found.component.html pour rediriger vers home.
Solution
4
git checkout -f step-04-solution
Des questions ?
Exercice
5
git checkout -f step-05
-
Créez un resolver "HomeResolver" :
-
Déplacez le tableau introductions dans le resolver
- Récupérez l'id de la route à l'aide de la classe ActivatedRoute
- Retournez le bon profil à l'aide de la fonction _find de lodash (https://lodash.com/docs/#find) en ajoutant l'import ( import * as _ from 'lodash'; )
-
Récupérez la données dans le composant HomeComponent à l'aide de la classe ActivatedRoute
-
-
Ajoutez un guard "ConnectedGuard" dans le module SecurityModule pour interdire l'accès à la page Skills
Solution
5
git checkout -f step-05-solution
Des questions ?
Service

Injection de dépendance
import { Engine, Tires, Doors } from './shared';
class Car {
constructor(
private _engine: Engine,
private _tires: Tires,
private _doors: Doors
) {
}
}
Typescript
Injection de dépendance
● L’Injector expose l’API pour créer des instances de dépendances
● Le Provider indique à l’Injector comment créer la dépendance
● La dépendance est le type d’objet à créer
Injection de dépendance
-
Par défaut singleton
-
Pour avoir des instances
-
Utiliser une Factory
-
Ou créer un Provider fils
-
Les providers
Services
import { Injectable } from '@angular/core';
@Injectable()
export class MyService {
name: string;
constructor() {
this.name = "Angular";
}
getName(): string {
return this.name;
}
}Déclaration
-
Une simple classe exportée
-
Un décorateur @Injectable()
Services
import { Component, OnInit } from '@angular/core';
import {MyService} from './my.service';
@Component({
selector: 'app-my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss'],
providers: [MyService] //Ou {provide: MyService, {useClass: MyService}}
})
export class MyComponent implements OnInit {
constructor(private myService: MyService) {
}
ngOnInit() {}
}Injection locale
Services
import { NgModule } from '@angular/core';
import {SharedModule} from '../shared/shared.module';
import {MyResolverService} from './my-resolver.service';
import {MyService} from './my.service';
@NgModule({
imports: [
SharedModule
],
declarations: [],
exports: [],
providers: [MyResolverService, MyService] //Ou {provide: MyService, {useClass: MyService}}
})
export class MyModule { }
Injection globale
Communication serveur
import { NgModule } from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
@NgModule({
imports: [HttpClientModule],
...
});
Intégrer HttpModule dans notre NgModule
Communication serveur
import { Injectable } from '@angular/core';
import {HttpBackend, HttpClient} from '@angular/common/http';
@Injectable()
export class MyService {
constructor(private http: HttpClient) { }
getSkills(id: number): Observable<Skill>{
const headers = { 'Authorization': 'secret' };
const params = { 'page': '1' };
return this.http.get<Skill>('/api/skills', { headers, params });
}
}
Importer le service HttpClient
RxJs
Reactive extensions for JavaScript
Une librairie de flux reactifs qui vous permet de travailler avec des flux de données asynchrones.
Le Pattern Observer
RxJs
Reactive extensions for JavaScript
-
Flux événements
-
Un événement peut être une valeur, une erreur ou une terminaison
-
On peut s'abonner au flux
-
Observables != Promises
RxJs
Reactive extensions for JavaScript
var observer = rx.Observer.create(
function onNext(result){
console.log(result);
},
function onError(err){
console.log(err);
},
function onCompleted(){
console.log('Completed');
}
);
observable.subscribe(observer);Création
RxJs
Reactive extensions for JavaScript
Transformation
Filter
RxJs
Reactive extensions for JavaScript
Transformation
Map
RxJs
Reactive extensions for JavaScript
Lecture d'un marble diagram
Map

RxJs
Reactive extensions for JavaScript
Pipe avec des littable operators
-
littable operators => Let
-
Similaire à l'opérateur de map
-
Il permet de composer plusieurs opérations
RxJs
Reactive extensions for JavaScript
import { ajax } from "rxjs/observable/dom/ajax";
import { of } from "rxjs/observable/of";
import { catchError, map } from "rxjs/operators";
const name = ajax
.getJSON<{ name: string }>("/api/employees/alice")
.pipe(
map(employee => employee.name),
catchError(error => of(null))
);Communication serveur
import { Injectable } from '@angular/core';
import {HttpBackend, HttpClient} from '@angular/common/http';
@Injectable()
export class MyService {
constructor(private http: HttpClient) { }
getSkills(id: number): Observable<string>{
const headers = { 'Authorization': 'secret' };
const params = { 'page': '1' };
return this.http
.get<Skill>('/api/skills', { headers, params })
.pipe(
map(skill => skill.name),
catchError(err => of(null))
);
}
}
HttpClient Observable
Unit test

Jasmine est un Framework de test JavaScript orienté Behavior-Driven Development, ou BDD.
Karma est un outil qui nous permet d'exécuter des navigateurs et d'exécuter des tests Jasmine
//helloWorld.ts
function helloWorld() {
return 'Hello world!';
}
//helloWorld.spec.ts
describe('Hello world', () => { (1)
it('says hello', () => { (2)
expect(helloWorld()) (3)
.toEqual('Hello world!'); (4)
});
});Exemple:
Unit test

1 - describe(string, function): définit une série de tests
2 - it(string, function): définit un test individuel
3- expect(actual): expectation, ça décrit le comportement attendu
matcher(expected): la valeur attendu, une comparaison est faite
4 - expected: le test casse si le "actual" n'égale pas le "expected"
describe('Hello world', () => { (1)
it('says hello', () => { (2)
expect(helloWorld()) (3)
.toEqual('Hello world!'); (4)
});
});Exercice
6
git checkout -f step-06
-
Créez un service : HomeService
-
Refactoring: Déplacez le code de récupération des introductions dans ce service -
Injectez le HomeService dans le HomeResolver afin de récupérer les introductions
Solution
6
git checkout -f step-06-solution
Exercice
7
git checkout step-07
Le répertoire _server contient des services REST avec les endpoints suivants :
/**
* Introductions
*/
app.get('/api/intros', api.getIntros);
app.get('/api/intros/:id', api.getIntro);
app.put('/api/intros/:id', api.updateIntro);
/**
* Skills
*/
app.get('/api/skills', api.getSkills);
app.put('/api/skills/:id', api.updateSkill);
app.post('/api/skills', api.addSkill);
app.delete('/api/skills/:id', api.deleteSkill);
Exercice
7
git checkout step-07
# cd _server
# npm start
✔Express server listening on http://localhost:9000/
Démarrer le serveur


http://localhost:9000/api/intros
#npm installExercice
7
On veut appeler directement le endpoint /api/intros/:id
-
Améliorez le service HomeService
-
Améliorez le resolver HomeResolver
-
Utilisez le pipe et les opérations lettable pour gérer l'exception 404
-
#npm startSolution
7
git checkout -f step-07-solution
Des questions ?
Component
Input & Output

State management

State management
import {Component, OnInit, Output, Input, EventEmitter} from '@angular/core';
import {FormGroup, FormControl} from "@angular/forms";
@Component({
selector: 'app-intro-form',
templateUrl: 'form.component.html',
styleUrls: ['form.component.scss']
})
export class IntroFormComponent implements OnInit {
@Input() model:any = {};
@Output() submit;
constructor() {
this.submit = new EventEmitter();
}
ngOnInit() {
console.log(model);
}
submit(model){
this.submit.emit(model);
}State management
// parent.component.html
<app-intro-form [model]="model" (submit)="save($event)" > </app-intro-form>Exercice
8
git checkout -f step-08
-
Créez un composant FormComponent à l'intérieur du dossier home
-
Ajoutez un input (model) et deux outputs (submit$ et cancel$)
-
Affichez le model (introduction) dans le form.component.html et créez deux boutons pour envoyer les deux évènements (submit$ et cancel$)
-
Affichez le composant FormComponent dans le template home.component.html avec les bons paramètres.
Solution
8
git checkout -f step-08-solution
Forms

Formulaires
La gestion des formulaires a toujours été un des points forts d'Angular
Template Driven Forms Pilotée par les templates (simple) Model Driven Forms
Pilotée par le code (plus verbeux mais plus puissant)
Formulaires
Un champ de formulaire est un FormControl :
valid : si le champ est valide avec les validations et contraintes qui lui sont appliquées errors : objet contenant les erreurs du champ dirty : false jusqu'à ce que l'utilisateur modifie le champ pristine : opposé de dirty touched : false jusqu'à ce que l'utilisateur soit entré et sorti du champ untouched : opposé de touched value : la valeur du champ valueChanges : un observable emettant la valeur du champ à chaque changement
Formulaires
Créer un champ de formulaire
const username = new FormControl('', {
validators: Validators.required,
updateOn: blur}); // Update only when remove focus
console.log(username.dirty);
console.log(username.hasError('required')); //trueNew
Formulaires
Un formulaire est représenté par un FormGroup :
valid : si le formulaire est valide errors : objet contenant les erreurs du formulaire dirty : false jusqu'à ce que l'utilisateur modifie un des champs du formulaire pristine : opposé de dirty touched : false jusqu'à ce que l'utilisateur soit entré et sorti d'un des champs du formulaire untouched : opposé de touched value : la valeur du formulaire sous forme de clé/valeur avec le nom du champ en clé valueChanges : un observable emettant la valeur du formulaire à chaque changement sur un des champs
Formulaires
Créer un groupe de champs
const form = new FormGroup({
username: new FormControl(),
password: new FormControl()
});
console.log(form.dirty);
console.log(form.value);
console.log(form.get('username'));
Formulaires
Reactive Form
@Component({
selector: 'app-react-form',
templateUrl: './react-form.component.html'
})
export class ReactFormComponent {
loginForm: FormGroup;
constructor(formBuilder: FormBuilder) {
this.loginForm = formBuilder.group({username: '', password: ''});
}
register() {
console.log(this.loginForm.value);
}
}
Formulaires
Reactive Form
<form (ngSubmit)="register(loginForm.value)" [formGroup]="loginForm">
<div class="form-group">
<label for="username">Nom d'utilisateur</label>
<input type="text" class="form-control"
id="username" formControlName="username"
placeholder="Nom d'utilisateur">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control"
id="password" formControlName="password"
placeholder="Mot de passe">
</div>
<button type="submit" class="btn btn-success">Se connecter</button>
</form>Formulaires
On peut être notifié des changements grâce aux observables
@Component({
selector: 'app-react-form',
templateUrl: './react-form.component.html'
})
export class ReactFormComponent {
loginForm: FormGroup;
constructor(formBuilder: FormBuilder) {
this.loginForm = formBuilder.group({username: '', password: ''});
this.loginForm.valueChanges.subscribe(form => console.log(form));
this.loginForm.get('username').valueChanges
.subscribe(username => console.log(username));
}
register() {
console.log(this.loginForm.value);
}
}
Formulaires
Validateurs: Retourne une map d'erreurs ou 'null' si aucune erreur est détecté
Quelques validateurs:
required: permet de vérifier que le champ n'est pas vide
minLength: permet de vérifier la longueur minimum
maxLength: permet de vérifier la longueur maximum
pattern: permet de vérfier le respect d'une regex
Formulaires
@Component({
selector: 'app-react-form',
templateUrl: './react-form.component.html'
})
export class ReactFormComponent {
loginForm: FormGroup;
constructor(formBuilder: FormBuilder) {
this.loginForm = formBuilder.group({
username: formBuilder.control('', [Validators.required,Validators.minLength(8)]),
password: formBuilder.control('', [Validators.required,Validators.pattern("[^ @]*@[^ @]*")])
});
}
}
Validateurs
Formulaires
Affichage des erreurs
<form (ngSubmit)="register(loginForm.value)" [formGroup]="loginForm">
<div class="form-group">
<label for="username">Nom d'utilisateur</label>
<mat-input-container
[ngClass]="{required: loginForm.get('username').hasError('required')}">
<input type="text" class="form-control"
id="username" formControlName="username"
placeholder="Nom d'utilisateur">
<mat-hint align="start" *ngIf="loginForm.get('username').hasError('required')">
La saisie du nom d'utilisateur est obligatoire
</mat-hint>
</mat-input-container>
</div>
<button type="submit" class="btn btn-success">Se connecter</button>
</form>Formulaires
Custum validator
export class ValidFormComponent {
loginForm: FormGroup;
constructor(formBuilder: FormBuilder) {
const isKnowUser= (control: FormControl) =>
['jmadih', 'rinfrayco']
.filter(v => v === control.value).length ? null : {unknownUser: true};
this.loginForm = formBuilder.group({
username: formBuilder.control('', [Validators.required,
Validators.minLength(3), isKnowUser]),
password: formBuilder.control('', Validators.required)
});
}
}Si la fonction renvoie un observable la validation deviendra asynchrone.
Exercice
9
git checkout -f step-09
-
Initiez un FormGroup pour le formulaire Introduction (que les champs visibles sur home) au niveau du service SouscriptionInitFormService
-
Ajoutez le validateurs required pour tous les champs et un validateur minLength(20) pour le champs description
Exercice
9
git checkout step-09
-
Initialiser le formulaire dans le template form.component.html (formGroup, fromControlName)
-
Afficher les messages d'erreurs grâce aux composant mat-hint
-
Appeler le endpoint put '/intros/:id' pour modifier le formulaire après le clique sur le bouton save
Solution
9
git checkout -f step-09-solution
Restart Rest server
Des questions ?
Pipes

Permet de filtrer, transformer, tronquer, ... des données brutes
Possibilité de chainer les pipes
Possibilité de prendre des paramètres
uppercase et lowercase
<p>{{ 'Formation Angular' | uppercase}}</p>
Affichera 'FORMATION ANGULAR'
<p>{{ 'Formation Angular' | lowercase}}</p>
Affichera 'formation angular'Affiche en majuscules ou minuscules une chaine de caractères
Slice
<p>{{users | slice:0:2 | json}}</p>
Affichera les 2 premiers éléments du tableau
<p>{{ 'Formation Angular' | slice:0:9}}</p> <!-- Formation -->
Affichera 'Formation'
<p>{{ 'Formation Angular' | slice:-7}}</p> <!-- Angular -->
Affichera 'Angular'Renvoi un sous-ensemble d'un tableau ou chaine de caractères
Percent
<p>{{ 0.8 | percent }}</p> <!-- 80% -->
<p>{{ 0.8 | percent:'.3' }}</p> <!-- 80.000% -->Permet d'afficher ... un pourcentage
Number
<p>{{ 12345 | number }}</p> <!-- 12,345 -->
<p>{{ 12345 | number:'9.' }}</p> <!-- 000,012,345 -->
<p>{{ 12345 | number:'.2' }}</p> <!-- 12,345.00 -->
<p>{{ 12345.14 | number:'.1-1' }}</p> <!-- 12,345.1 -->
<p>{{ 12345.16 | number:'.1-1' }}</p> <!-- 12,345.2 -->Permet de formater un nombre
Prend un seul param sous la forme :
{integerDigits}.{minFractionDigits}-{maxFractionDigits}
Json
<p>{{ users }}</p>
affichera [object Object].
Pour afficher la représentation JSON de l'objet :
<p>{{ users | json }}</p>
Équivalent de JSON.stringifyDate
<p>{{ today | date:'dd/MM/yyyy'}}</p> <!-- 05/02/2017 -->
<p>{{ today | date:'longDate'}}</p> <!-- February 5, 2017 -->
<p>{{ today | date:'HH:mm:ss'}}</p> <!-- 13:48:43 -->
<p>{{ today | date:'shortTime'}}</p> <!-- 1:48 PM -->Permet de formater les dates
Currency
<p>{{ 20.8 | currency:'EUR' }}</p> <!-- EUR20.80 -->
<p>{{ 20.8 | currency:'USD':true }}</p> <!-- $20.80 -->
<p>{{ 20.8 | currency:'USD':true:'.3' }}</p> <!-- $20.800 -->Permet d'afficher des monnaies avec les paramètres suivants:
- le code ISO de la devise ('EUR', 'USD')
- un booléen indiquant si on souhaite utiliser le symbole ('€', '$')
- la chaîne de formatage du montant dans le même format que *number*
Async
@Component({
selector: 'app-pipes',
template: `
<p> {{usersName$ | async}}</p>
`
})
export class MyComponent {
usersName$: Observable<string>;
}Permet d'afficher des données obtenues de manière asynchrone
Le pipe se charge de se désabonner de la source tout seul.
Custum Pipe
@Pipe({
name: 'fromNow'
})
export class FromNowPipe implements PipeTransform {
transform(value: Date): string {
return moment(value).fromNow();
}
}
usage
{{myDate | fromNow}}Exercice
10
-
Créez un pipe UndefinedPipe qui permet d'afficher 'N/A' si aucune valeur n'est renseigner
-
Appliquez ce pipe sur le champs mail de l'introduction
Solution
10
git checkout -f step-10-solution

WebComponents (Angular Element)

Angular Element


Et si on pouvait déployer mon widget Angular sur n'importe quel site ?

Angular Element

Ne serait-il pas formidable de pouvoir déployer une nouvelle version de notre widget indépendamment du cycle de publication des grandes applications?
Angular Element






HTTP
WebComponent A
WebComponent B
Application
main.js
main.js
Angular Element
import {BrowserModule} from '@angular/platform-browser';
import {DoBootstrap, Injector, NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {createCustomElement} from '@angular/elements';
import {NgCardElementComponent} from './ng-card-element/ng-card-element.component';
@NgModule({
declarations: [
AppComponent
],
entryComponents: [NgCardElementComponent],
imports: [
BrowserModule
]
})
export class AppModule implements DoBootstrap {
constructor(private injector: Injector) {}
ngDoBootstrap() {
const el = createCustomElement(NgCardElementComponent, {
injector: this.injector
});
customElements.define('times-up', el);
}
}
Déclaration du WebComponent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Time's UP</title>
</head>
<body>
<times-up endtime="27/02/2020 18:00:00" title="Title"
subtitle="Subtitle" description="description">
</times-up>
</body>
<script src="URL/main.js"></script>
</html>
Angular Element
Utilisation du WebComponent
Libre
TP
CRUD sur la page skills
Autres concepts
LazyLoading,
Animation, I18N, Material design, Web worker, RXJS
Server side rendering (Universal)
Liens utiles
TH NK YOU FOR COMING


Angular
By Sydelab - Shaping Tomorrow
Angular
- 690

