33 2 2MB
Angular 11 : La Synthèse
Mohamed Youssfi Laboratoire Signaux Systèmes Distribués et Intelligence Artificielle (SSDIA) ENSET, Université Hassan II Casablanca, Maroc Email : [email protected] Supports de cours : http://fr.slideshare.net/mohamedyoussfi9 Chaîne vidéo : http://youtube.com/mohamedYoussfi Recherche : http://www.researchgate.net/profile/Youssfi_Mohamed/publications
Créer une application qui permet de gérer des produits :
Partie Backend de Test : Json-server Bases fondamentales de Angular Ractive Forms Comment décomposer un Component Différentes façon de Communication entre les components Quelques bonnes pratiques : Transition vers NGRX pour le State Management
db.json {
"bootstrap": "^4.5.3", "concurrently": "^5.3.0", "font-awesome": "^4.7.0", "jquery": "^3.5.1", "json-server": "^0.16.3", "scripts": { "ng": "ng", "start": "concurrently \"ng serve\" "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" },
"products": [ { "id": 1, "name": "Computer", "price": 21000, "quantity": 89, "available": true, "selected": false }, { "id": 2, "name": "Printer", "price": 6500, "quantity": 8, "selected": true, "available": true }, { "id": 3, "name": "Smart Phone", "price": 12000, "quantity": 21, "selected": false, "available": true } ]
\"json-server --watch db.json\"",
}
ProductsComponent Single Component
ProductNavBarComponent
Products Component ProductNavBar Component
ProductList Component ProductItem Component
ProductItem Component
ProductListComponent
ProductItemComponent
ProductsComponent Single Component
Products Component ProductNavBar Component
ProductNavBarComponent
Data ProductList Component
ProductItem Component
ProductItem Component
ProductListComponent
ProductItemComponent
ProductsComponent ProductNavBarComponent Single Component
Products Component ProductNavBar Component
Subscribe Data
ProductListComponent
ProductList Component
Publish
EventSubjectService Subject Observable
ProductItem Component
ProductItem Component
ProductItemComponent
product.model.ts
product.actions.ts
export interface Product { id:number; name:string; price:number; quantity:number; selected:boolean; available:boolean; }
export enum ProductQueryActions { GET_ALL_PRODUCTS="GET_ALL_PRODUCTS", GET_SELECTED_PRODUCTS="GET_SELECTED_PRODUCTS", GET_AVAILABLE_PRODUCTS="GET_AVAILABLE_PRODUCTS", EDIT_PRODUCT="EDIT_PRODUCT", SEARCH_PRODUCT="SEARCH_PRODUCT", NEW_PRODUCT="NEW_PRODUCT" } export enum ProductCommandActions { ADD_PRODUCT="ADD_PRODUCT", DELETE_PRODUCT="DELETE_PRODUCT", UPDATE_PRODUCT="UPDATE_PRODUCT", SELECT_PRODUCT="SELECT_PRODUCT", EDIT_PRODUCT="EDIT_PRODUCT", }
product.state.ts export enum DataStateEnum { LOADING, LOADED, ERROR, } export interface AppDataState { dataState: DataStateEnum; data?: T, errorMessage?:string }
export interface ActionEvent{ type:A; payload?:T; }
environement.ts product.service.ts
@Injectable({providedIn:"root"}) export class ProductService {
export const environment = { production: false, host:"http://localhost:3000", unreachableHost:"http://localhost:3008" };
constructor(private http:HttpClient) { } public getProducts():Observable{ let host=Math.random()>0.2?environment.host:environment.unreachableHost; return this.http.get(host+"/products"); //return throwError("Not Implemented yet"); } public getSelectedProducts():Observable{ //if(Math.random()>0.1) return throwError({message:"Internal Error"}); //else return this.http.get(environment.host+"/products?selected=true"); } public getAvailableProducts():Observable{ return this.http.get(environment.host+"/products?available=true"); }
product.service.ts
public searchProducts(name:string):Observable{ return this.http.get(environment.host+"/products?name_like="+name); } public setSelected(product:Product):Observable{ product.selected=!product.selected; return this.http.put(environment.host+"/products/"+product.id,product); } public delete(id:number):Observable{ return this.http.delete(environment.host+"/products/"+id); } public save(product:Product):Observable{ return this.http.post(environment.host+"/products/",product); } public update(product:Product):Observable{ return this.http.put(environment.host+"/products/"+product.id,product); } public getProductById(id:number):Observable{ return this.http.get(environment.host+"/products/"+id); } }
Event.driven.service.ts
import {Injectable} from '@angular/core'; import {Subject} from 'rxjs'; import {ActionEvent, ProductCommandActions, ProductQueryActions} from '../state/state'; @Injectable({providedIn:"root"}) export class EventDrivenService { private queryEventSource=new Subject(); queryEventSourceObservable=this.queryEventSource.asObservable(); private commandEventSource=new Subject(); commandEventSourceObservable=this.commandEventSource.asObservable(); public publishQueryAction(action :ActionEvent){ this.queryEventSource.next(action); } public publishCommandAction(action :ActionEvent){ this.commandEventSource.next(action); } }
NgRx est une librairie implémentant le pattern Redux en utilisant la programmation réactive basée sur RxJS.
NgRX permet à une application Angular de centraliser l’état de l’application dans un unique Objet.
Tous les composants de l’application peuvent accéder facilement à l’état de l’application d’une manière réactive.
Chaque composant peut faire une souscription aux données du State dont il a besoin en utilisant des sélectors.
À chaque fois que ces données du state changent le composant est mis à jour d’une manière réactive (Real Time)
Ce mécanisme de NgRx propose des mécanisme puissant alternatifs aux solutions classiques fournies par Angular tels que
@Input et @Output permettant de faire communiquer d’une manière simple les web composants à travers l’arbre des composants, mais qui s’avère parfois complexe pour une grande application
Les services qui peuvent être utilisés pour partager les données de l’applications et aussi les traitements pour l’ensembles de composants web de l’application
NgRX est une implémentation du Pattern Redux en utilisant RxJS.
L’architecture de NgRX repose sue les composants suivants:
Store : Un objet JavaScript contenant l’état (State) de votre application. Le State est un Objet Java Script immutable.
Web Components : Tout composant web de l’application qui souhaite utiliser une partie de l’état de l’application doit souscrire un abonnement dans le Store.
Selectors : pour permettre à un composant d’observer des parties précises du state au lieu d’observer tout le state, NgRx fournit le mécanisme des sélecteurs.
Actions : Ce sont les évènements émis par votre application au niveau des composants suite aux interactions IHM ou dans les services suite des interactions avec le Backend. Pour dispatcher une action ont utiliser l’objet store. Une action est un objet Java Script définit par deux attributs:
type : qui représente le type de l’événement de type string
payload : un objet java script contenant les paramètres de l’action.
Reduces : Un Reducer est une fonction java scripts pures représentant des écouteurs d’événements (Actions) qui reçoivent le state actuel et l’action dispatchée par le store. En fonction du type de l’action et son payload, le Reducer returne un nouveau State du Store, en permettant à l’application de passer d’un état courant vers un nouvel état. Les Reducers doivent préserver l’émmutabilité du state. Ce qui ouvre la possibilité de préserver l’historique des différents changements dans le state de l’application.
Effects : Un Effect est un observateur d’un certains types d’actions particulières qu’il faudrait intercepter pour faire appel aux services de l’applications qui sont souvent utilisés pour interagir avec le backend. Chaque interaction avec le backend entraine un événement (Success ou Error) qui est traduit par l’effect dispatchant une autre action destiné à être interceptée par un Reducer en vu de mettre à jour le state du store.
Entities : pour faciliter la gestion des collections d’entités du State, NgRX fournit un mécanisme qui permet d’ajouter, rechercher, mettre à jour et supprimer des entités du State de l’application en utilisant EntityFactory.
Component
Store
Reducer
State Observable
Effect
Services
Backend
Subscribe Subscribe
Observable
dispatch (action)
action
:Action
State View Date State
action
New State
State View State Status
State
dispatch (action) action
State New State
action
Data
REST
Créer une application qui permet de gérer des produits :
Partie Backend de Test : Json-server
State Management avec NGRX
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git
npm install --save bootstrap jquery font-awesome npm install --save json-server npm i --save concurrently
{ "products": [ {"id": 1,"name": "Computer","price": 60000,"quantity": 12,"selected": true,"available": true}, {"id": 2,"name": "Printer","price": 1200,"quantity": 10,"selected": true,"available": false}, {"id": 3,"name": "Smartphone","price": 2000,"quantity": 32,"selected": false,"available": true} ] }
"scripts": { "ng": "ng", "start": "concurrently \"ng serve\" \"json-server --watch db.json\"",
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git "styles": [ "src/styles.css", "node_modules/bootstrap/dist/css/bootstrap.min.css" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js" ]
@import "~font-awesome/css/font-awesome.min.css";
export interface Product { id:number; name:string; price:number; quantity:number; selected:boolean; available:boolean; }
imports: [ BrowserModule, AppRoutingModule, HttpClientModule ],
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git import import import import import
{Injectable} from '@angular/core'; {HttpClient} from '@angular/common/http'; {Observable} from 'rxjs'; {environment} from '../../environments/environment'; {Product} from '../model/product.model';
@Injectable({providedIn:"root"}) export class ProductService { constructor(private http:HttpClient) { } public getProducts():Observable{ let host=Math.random()>0.2?environment.host:environment.unreachableHost; //let host=environment.host; return this.http.get(host+"/products"); //return throwError("Not Implemented yet"); } public getSelectedProducts():Observable{ return this.http.get(environment.host+"/products?selected=true"); } public getAvailableProducts():Observable{ return this.http.get(environment.host+"/products?available=true"); }
public searchProducts(name:string):Observable{ return this.http.get(environment.host+"/products?name_like="+name); } public setSelected(product:Product):Observable{ return this.http.put(environment.host+"/products/"+product.id,{...pro duct,selected:!product.selected}); } public delete(id:number):Observable{ return this.http.delete(environment.host+"/products/"+id); } public save(product:Product):Observable{ return this.http.post(environment.host+"/products/",product); } public update(product:Product):Observable{ return this.http.put(environment.host+"/products/"+product.id,product ); } public getProductById(id:number):Observable{ return this.http.get(environment.host+"/products/"+id); } }
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git
ID | Name | Price | CategoryID | ||
---|---|---|---|---|---|
{{p.id}} | {{p.name}} | {{p.price}} | {{p.categoryID}} | Edit | Delete |
ID | Name | Price | CategoryID | ||
---|---|---|---|---|---|
{{p.id}} | {{p.name}} | {{p.price}} | {{p.categoryID}} | Edit | Delete |