100% found this document useful (4 votes)
268 views37 pages

Support Bonnes Pratiques de Angular Avec NGRX

The document discusses Angular 11 and provides an overview of creating an application to manage products using Angular fundamentals like reactive forms, component decomposition, and communication between components. It also demonstrates using an event-driven architecture with services, state management with NgRx, and some best practices. Code examples are provided for components, services, models, actions and reducing state changes.

Uploaded by

Rachid
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (4 votes)
268 views37 pages

Support Bonnes Pratiques de Angular Avec NGRX

The document discusses Angular 11 and provides an overview of creating an application to manage products using Angular fundamentals like reactive forms, component decomposition, and communication between components. It also demonstrates using an event-driven architecture with services, state management with NgRx, and some best practices. Code examples are provided for components, services, models, actions and reducing state changes.

Uploaded by

Rachid
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 37

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", "products": [
{
"font-awesome": "^4.7.0",
"id": 1,
"jquery": "^3.5.1", "name": "Computer",
"json-server": "^0.16.3", "price": 21000,
"quantity": 89,
"available": true,
"scripts": {
"selected": false
"ng": "ng", },
"start": "concurrently \"ng serve\" \"json-server --watch db.json\"", {
"build": "ng build", "id": 2,
"test": "ng test", "name": "Printer",
"lint": "ng lint", "price": 6500,
"quantity": 8,
"e2e": "ng e2e"
"selected": true,
}, "available": true
},
{
"id": 3,
"name": "Smart Phone",
"price": 12000,
"quantity": 21,
"selected": false,
"available": true
}
]
}
ProductsComponent

Single Component ProductNavBarComponent

Products
Component

ProductNavBar ProductList ProductListComponent ProductItemComponent


Component Component

ProductItem ProductItem
Component Component
ProductsComponent

Single Component ProductNavBarComponent

Products
Data
Component

ProductNavBar ProductList ProductListComponent ProductItemComponent


Component Component

ProductItem ProductItem
Component Component
ProductsComponent

ProductNavBarComponent

Single Component

Subscribe
Products
Component Data ProductListComponent ProductItemComponent

Publish EventSubjectService
ProductNavBar ProductList
Component Component Subject
Observable

ProductItem ProductItem
Component Component
product.model.ts product.actions.ts
export interface Product { export enum ProductQueryActions {
id:number; GET_ALL_PRODUCTS="GET_ALL_PRODUCTS",
name:string; GET_SELECTED_PRODUCTS="GET_SELECTED_PRODUCTS",
GET_AVAILABLE_PRODUCTS="GET_AVAILABLE_PRODUCTS",
price:number;
EDIT_PRODUCT="EDIT_PRODUCT",
quantity:number; SEARCH_PRODUCT="SEARCH_PRODUCT",
selected:boolean; NEW_PRODUCT="NEW_PRODUCT"
available:boolean; }
} export enum ProductCommandActions {
ADD_PRODUCT="ADD_PRODUCT",
product.state.ts DELETE_PRODUCT="DELETE_PRODUCT",
UPDATE_PRODUCT="UPDATE_PRODUCT",
export enum DataStateEnum { SELECT_PRODUCT="SELECT_PRODUCT",
LOADING, EDIT_PRODUCT="EDIT_PRODUCT",
LOADED, }
ERROR,
} export interface ActionEvent<A,T>{
type:A;
export interface AppDataState<T> { payload?:T;
dataState: DataStateEnum; }
data?: T,
errorMessage?:string
}
environement.ts
export const environment = {
product.service.ts production: false,
host:"http://localhost:3000",
@Injectable({providedIn:"root"})
unreachableHost:"http://localhost:3008"
export class ProductService {
};
constructor(private http:HttpClient) {
}

public getProducts():Observable<Product[]>{
let host=Math.random()>0.2?environment.host:environment.unreachableHost;
return this.http.get<Product[]>(host+"/products");
//return throwError("Not Implemented yet");
}
public getSelectedProducts():Observable<Product[]>{
//if(Math.random()>0.1) return throwError({message:"Internal Error"});
//else
return this.http.get<Product[]>(environment.host+"/products?selected=true");
}
public getAvailableProducts():Observable<Product[]>{
return this.http.get<Product[]>(environment.host+"/products?available=true");
}
product.service.ts
public searchProducts(name:string):Observable<Product[]>{
return this.http.get<Product[]>(environment.host+"/products?name_like="+name);
}
public setSelected(product:Product):Observable<Product>{
product.selected=!product.selected;
return this.http.put<Product>(environment.host+"/products/"+product.id,product);
}
public delete(id:number):Observable<void>{
return this.http.delete<void>(environment.host+"/products/"+id);
}
public save(product:Product):Observable<Product>{
return this.http.post<Product>(environment.host+"/products/",product);
}
public update(product:Product):Observable<Product>{
return this.http.put<Product>(environment.host+"/products/"+product.id,product);
}
public getProductById(id:number):Observable<Product>{
return this.http.get<Product>(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<ActionEvent<ProductQueryActions,any>>();
queryEventSourceObservable=this.queryEventSource.asObservable();
private commandEventSource=new Subject<ActionEvent<ProductCommandActions,any>>();
commandEventSourceObservable=this.commandEventSource.asObservable();

public publishQueryAction(action :ActionEvent<ProductQueryActions,any>){


this.queryEventSource.next(action);
}
public publishCommandAction(action :ActionEvent<ProductCommandActions,any>){
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.  Effects : Un Effect est un observateur d’un certains
 L’architecture de NgRX repose sue les composants suivants: types d’actions particulières qu’il faudrait intercepter
pour faire appel aux services de l’applications qui
 Store : Un objet JavaScript contenant l’état (State) de votre application. Le State
sont souvent utilisés pour interagir avec le backend.
est un Objet Java Script immutable.
Chaque interaction avec le backend entraine un
 Web Components : Tout composant web de l’application qui souhaite utiliser événement (Success ou Error) qui est traduit par
une partie de l’état de l’application doit souscrire un abonnement dans le Store. l’effect dispatchant une autre action destiné à être
 Selectors : pour permettre à un composant d’observer des parties précises du interceptée par un Reducer en vu de mettre à jour le
state au lieu d’observer tout le state, NgRx fournit le mécanisme des sélecteurs. state du store.
 Actions : Ce sont les évènements émis par votre application au niveau des  Entities : pour faciliter la gestion des collections
composants suite aux interactions IHM ou dans les services suite des d’entités du State, NgRX fournit un mécanisme qui
interactions avec le Backend. Pour dispatcher une action ont utiliser l’objet permet d’ajouter, rechercher, mettre à jour et
store. Une action est un objet Java Script définit par deux attributs: supprimer des entités du State de l’application en
utilisant EntityFactory.
 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.
Component Store Reducer Effect Services Backend
State
Subscribe
Observable
Subscribe
<Action>

Observable
<State>

dispatch (action) action State


action
:Action
New State
State
REST
View dispatch (action) action Data
State Status
action State

New State
State

View
Date State
 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", export interface Product {
"node_modules/bootstrap/dist/css/bootstrap.min.css" id:number;
], name:string;
"scripts": [ price:number;
"node_modules/jquery/dist/jquery.min.js", quantity:number;
"node_modules/bootstrap/dist/js/bootstrap.min.js" selected:boolean;
] available:boolean;
}

 
@import "~font-awesome/css/font-awesome.min.css";
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git
public searchProducts(name:string):Observable<Product[]>{
 return
this.http.get<Product[]>(environment.host+"/products?name_like="+name);
}
import {Injectable} from '@angular/core'; public setSelected(product:Product):Observable<Product>{
import {HttpClient} from '@angular/common/http'; return
import {Observable} from 'rxjs'; this.http.put<Product>(environment.host+"/products/"+product.id,{...pro
import {environment} from '../../environments/environment'; duct,selected:!product.selected});
import {Product} from '../model/product.model'; }
public delete(id:number):Observable<void>{
@Injectable({providedIn:"root"}) return this.http.delete<void>(environment.host+"/products/"+id);
export class ProductService { }
public save(product:Product):Observable<Product>{
constructor(private http:HttpClient) { return
}
this.http.post<Product>(environment.host+"/products/",product);
}
public getProducts():Observable<Product[]>{
public update(product:Product):Observable<Product>{
let host=Math.random()>0.2?environment.host:environment.unreachableHost;
//let host=environment.host; return
return this.http.get<Product[]>(host+"/products"); this.http.put<Product>(environment.host+"/products/"+product.id,product
//return throwError("Not Implemented yet"); );
} }
public getSelectedProducts():Observable<Product[]>{ public getProductById(id:number):Observable<Product>{
return this.http.get<Product[]>(environment.host+"/products?selected=true"); return this.http.get<Product>(environment.host+"/products/"+id);
} }
public getAvailableProducts():Observable<Product[]>{
return this.http.get<Product[]>(environment.host+"/products?available=true"); }
}
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git


<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<!-- Brand -->
<a class="navbar-brand" href="#">Logo</a>

<!-- Links -->


<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Link 1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 2</a>
</li>

<!-- Dropdown -->


<li class="nav-item dropdown">
$ npm start
<a class="nav-link dropdown-toggle" href="#" id="navbardrop" data-toggle="dropdown">
Dropdown link
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Link 1</a>
<a class="dropdown-item" href="#">Link 2</a>
<a class="dropdown-item" href="#">Link 3</a>
</div>
</li>
</ul>
</nav>
https://github.com/mohamedYoussfi/angular-ngrx-products-app.git
$ npm start

> [email protected] start D:\Docs3\apps\ang11\ngrx-app


> concurrently "ng serve" "json-server --watch db.json"

[1]
[1] \{^_^}/ hi!
[1] Loading db.json
[1] Done
[1] Resources
[1] http://localhost:3000/products
[1] Home
[1] http://localhost:3000
[1] Type s + enter at any time to create a snapshot of the database
[1] Watching...
[0] - Generating browser application bundles...
[0] √ Browser application bundle generation complete.
[0] Initial Chunk Files | Names | Size
[0] vendor.js | vendor | 2.76 MB
[0] styles.css, styles.js | styles | 519.03 kB
[0] polyfills.js | polyfills | 485.30 kB
[0] scripts.js | scripts | 149.40 kB
[0] main.js | main | 13.83 kB
[0] runtime.js | runtime | 6.15 kB
[0] | Initial Total | 3.90 MB
[0] Build at: 2021-02-28T09:17:55.052Z - Hash: 7be137dc3c31e73fa8c7 - Time: 6032ms
[0] ** Angular Live Development Server is listening on localhost:4200, open your browser on
http://localh
ost:4200/ **
[0]
[0]
√ Compiled successfully.
 Dépendances à installer :
 npm install --save bootstrap jquery
 npm install --save json-server
 npm i --save @ngrx/store
 npm i --save @ngrx/effects
 npm i --save @ngrx/entity
 npm i --save @ngrx/store-devtools
 npm i --save concurrently

 db.json :
{
"products": [
{"id": 1,"name": "Computer Mac Book","price": 20000,"categotyID":1},
{"id": 2,"name": "Computer HP 45","price": 10000,"categotyID":1},
{"id": 3,"name": "Printer Epson LX","price": 3000,"categotyID":2}
],
"categories": [
{"id": 1,"name": "Computer"},
{"id": 1,"name": "Printer"}
]
}

 package.json :

"scripts": {
"ng": "ng",
"start": "concurrently \"ng serve\" \"json-server --watch db.json\"",
$ npm run start
 angular.json :
"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"
]

Dépendances à installer :
 ng g c home
 ng g c navbar
 ng g m products
 ng g c products/products -m products
 ng g c products/products-create -m products
 ng g c products/products-edit -m products
 ng g c products/products-list -m products
 ng g m categories
 ng g c categories/categories -m categories
 ng g c categories/categories-create -m categories
 ng g c categories/categories-edit -m categories
 ng g c categories/categories-list -m categories
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { NavbarComponent } from './navbar/navbar.component';

@NgModule({
declarations: [
AppComponent,
HomeComponent,
NavbarComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [


{path: 'products', loadChildren:()=>import("./products/products.module").then((m)=>m.ProductsModule) },
{path: 'categories', loadChildren:()=>import("./categories/categories.module").then((m)=>m.CategoriesModule) }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductsComponent } from './products/products.component';
import { ProductsCreateComponent } from './products-create/products-create.component';
import { ProductsEditComponent } from './products-edit/products-edit.component';
import {RouterModule, Routes} from '@angular/router';
import { ProductsListComponent } from './products-list/products-list.component';

const productsRoutes:Routes=[
{path:"",component:ProductsComponent}
]

@NgModule({
declarations: [ProductsComponent, ProductsCreateComponent, ProductsEditComponent, ProductsListComponent],
imports: [
CommonModule, RouterModule.forChild(productsRoutes)
]
})
export class ProductsModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CategoriesComponent } from './categories/categories.component';
import {RouterModule, Routes} from '@angular/router';
import {ProductsComponent} from '../products/products/products.component';
import { CategoriesCreateComponent } from './categories-create/categories-create.component';
import { CategoriesEditComponent } from './categories-edit/categories-edit.component';
import { CategoriesListComponent } from './categories-list/categories-list.component';

const categoriesRoutes:Routes=[
{path:"",component:CategoriesComponent}
]

@NgModule({
declarations: [CategoriesComponent, CategoriesCreateComponent, CategoriesEditComponent,
CategoriesListComponent],
imports: [
CommonModule, RouterModule.forChild(categoriesRoutes)
]
})
export class CategoriesModule { }
<div class="container mt-3">
<app-products-create></app-products-create>
<hr>
<app-products-list></app-products-list>
<hr>
<app-products-edit></app-products-edit>
</div>
<div class="container mt-3">
<app-categories-create></app-categories-create>
<hr>
<app-categories-list></app-categories-list>
<hr>
<app-categories-edit></app-categories-edit>
</div>
product.module.ts

export interface Product {


id?:number;
name:string;
price:number;
categoryID:number;
}
app.state.ts

export interface AppState {


}

app.module.ts
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
StoreModule.forRoot({}), products.module.ts
EffectsModule.forRoot([]),
imports: [
StoreDevtoolsModule.instrument()
CommonModule, RouterModule.forChild(productsRoutes),
],
StoreModule.forFeature("catalog",productReducer),
EffectsModule.forFeature([ProductEffects]),
FormsModule, ReactiveFormsModule
]
product.module.ts

import { Injectable } from '@angular/core';


import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Product} from '../model/products.model';
import {environment} from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private http:HttpClient) { }
public getProducts():Observable<Product[]>{
return this.http.get<Product[]>(environment.backendHost+"/products");
}
public getProductByID(id:number):Observable<Product>{
return this.http.get<Product>(environment.backendHost+`/products/${id}`);
}
public addProduct(product:Product):Observable<Product>{
return this.http.post<Product>(environment.backendHost+`/products`,product);
}
public updateProduct(product:Product):Observable<Product>{
return this.http.patch<Product>(environment.backendHost+`/products/${product.id}`,product);
}
public deleteProduct(id:number){
return this.http.delete(environment.backendHost+`/products/${id}`);
}
}
products.actions.ts
import {Action} from '@ngrx/store';
import {Product} from '../../model/products.model';

export enum ProductActionsTypes {


LOAD_PRODUCTS="[Product] Load Products",
LOAD_PRODUCTS_SUCCESS="[Product] Load Products Success",
LOAD_PRODUCTS_ERROR="[Product] Load Products Error"
}
export class LoadProductsAction implements Action{
readonly type:string=ProductActionsTypes.LOAD_PRODUCTS;
constructor(public payload:any) {
}
}
export class LoadProductsActionSuccess implements Action{
readonly type:string=ProductActionsTypes.LOAD_PRODUCTS_SUCCESS;
constructor(public payload:Product[]) {
}
}

export class LoadProductsActionError implements Action{


readonly type:string=ProductActionsTypes.LOAD_PRODUCTS_ERROR;
constructor(public payload:string) {
}
}
export type ProductActions=

LoadProductsAction|LoadProductsActionSuccess|LoadProductsActionError;
products.affects.ts
import {Injectable} from '@angular/core';
import {Actions, Effect, ofType} from '@ngrx/effects';
import {ProductService} from '../product.service';
import {Observable, of} from 'rxjs';
import {Action} from '@ngrx/store';
import {LoadProductsActionError, LoadProductsActionSuccess, ProductActions,
ProductActionsTypes} from './product.actions';
import {catchError, map, mergeMap} from 'rxjs/operators';
import {Product} from '../../model/products.model';

@Injectable()
export class ProductEffects {
constructor(private effectActions:Actions, private productService:ProductService) {
}
@Effect()
loadProducts:Observable<Action>=this.effectActions.pipe(
ofType(ProductActionsTypes.LOAD_PRODUCTS),
mergeMap((action:ProductActions)=>
this.productService.getProducts()
.pipe(
map((products:Product[])=>new LoadProductsActionSuccess(products)),
catchError(err=>of(new LoadProductsActionError(err)))
)
)
);
}
products.affects.ts
import {Product} from '../../model/products.model';
import {ProductActions, ProductActionsTypes} from './product.actions';
import {AppState} from '../../state/app.state';

export interface ProductState extends AppState{


products:Product[];
loading:boolean;
loaded:boolean;
error:string;
}
const initialState:ProductState={
products:[],loaded:false,loading:false,error:null
}
export function
productReducer(state:ProductState=initialState,action:ProductActions):ProductState {
switch (action.type) {

case ProductActionsTypes.LOAD_PRODUCTS:
return {...state,loading:true}
case ProductActionsTypes.LOAD_PRODUCTS_SUCCESS:
return {...state,loaded:true,loading:false,products:action.payload}

case ProductActionsTypes.LOAD_PRODUCTS_ERROR:
return {...state,error:action.payload}
default: return {...state};
}
}
products-list.component.ts
import { Component, OnInit } from '@angular/core'; products-list.component.html
import {State, Store} from '@ngrx/store';
import {LoadProductsAction} from '../ngrxFeatures/product.actions'; <table class="table table-striped" *ngIf="products">
import {ProductState} from '../ngrxFeatures/product.reducer'; <thead>
import {Observable} from 'rxjs';
import {Product} from '../../model/products.model';
<tr>
import {AppState} from '../../state/app.state'; <th>ID</th><th>Name</th><th>Price</th><th>CategoryID</th>
import {map} from 'rxjs/operators'; </tr>
</thead>
@Component({ <tbody>
selector: 'app-products-list', <tr *ngFor="let p of products|async">
templateUrl: './products-list.component.html', <td>{{p.id}}</td>
styleUrls: ['./products-list.component.css'] <td>{{p.name}}</td>
}) <td>{{p.price}}</td>
export class ProductsListComponent implements OnInit { <td>{{p.categoryID}}</td>
products:Observable<Product[]>; <td><a (click)="editProduct(p)" class="btn btn-
constructor(private store:Store<any>) { } success">Edit</a></td>
<td><a (click)="deleteProduct(p)" class="btn btn-
ngOnInit(): void { danger">Delete</a></td>
this.store.dispatch(new LoadProductsAction({})); </tr>
this.products=this.store.pipe( </tbody>
map((state)=>state.catalog.products) </table>
);
}
deleteProduct(p: Product) { }
editProduct(p: Product) { }
}
products-list.component.ts
import { Component, OnInit } from '@angular/core'; products-list.component.html
import {State, Store} from '@ngrx/store';
import {LoadProductsAction} from '../ngrxFeatures/product.actions'; <table class="table table-striped" *ngIf="products">
import {ProductState} from '../ngrxFeatures/product.reducer'; <thead>
import {Observable} from 'rxjs';
import {Product} from '../../model/products.model';
<tr>
import {AppState} from '../../state/app.state'; <th>ID</th><th>Name</th><th>Price</th><th>CategoryID</th>
import {map} from 'rxjs/operators'; </tr>
</thead>
@Component({ <tbody>
selector: 'app-products-list', <tr *ngFor="let p of products|async">
templateUrl: './products-list.component.html', <td>{{p.id}}</td>
styleUrls: ['./products-list.component.css'] <td>{{p.name}}</td>
}) <td>{{p.price}}</td>
export class ProductsListComponent implements OnInit { <td>{{p.categoryID}}</td>
products:Observable<Product[]>; <td><a (click)="editProduct(p)" class="btn btn-
constructor(private store:Store<any>) { } success">Edit</a></td>
<td><a (click)="deleteProduct(p)" class="btn btn-
ngOnInit(): void { danger">Delete</a></td>
this.store.dispatch(new LoadProductsAction({})); </tr>
this.products=this.store.pipe( </tbody>
map((state)=>state.catalog.products) </table>
);
}
deleteProduct(p: Product) { }
editProduct(p: Product) { }
}
Angular Fundamentals STEP 0
Understand Angular Architecture

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
Angular Fundamentals STEP 0 Understand Angular Architecture

You might also like