Resultat du cours n°8

This commit is contained in:
Vincent BENOIT
2022-03-29 17:12:02 +02:00
parent 2a9b6425db
commit 6a6c27f98a
17 changed files with 267 additions and 8 deletions

24
package-lock.json generated
View File

@@ -1679,6 +1679,30 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@ngrx/effects": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-13.1.0.tgz",
"integrity": "sha512-b0kaC1yly1WawtQils3Bxy2FhE9OwlQD/bptVd3s+bskdZ+iw9VlAMH9Spk2mfX+MyOEOk2FyrWlrOePtkw+7Q==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngrx/store": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-13.1.0.tgz",
"integrity": "sha512-2Phsd/CC5FcoS2VgC+Fo5VgfAUK3m7bjWTc8d6+h3UcoQpS3xyPnybGYifa/JYN1CFYmqypVDRWSMAgML2NU/A==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngrx/store-devtools": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-13.1.0.tgz",
"integrity": "sha512-HAF2q1qk7n0Q3rkHncdXIokGXbPFKWwmfhGFS9pnq63FScgPgHjNXSlPmwgycb+LqDvDR4C8f1HSbPhOb6I29w==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngtools/webpack": {
"version": "13.2.6",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.6.tgz",

View File

@@ -18,6 +18,9 @@
"@angular/platform-browser": "~13.2.0",
"@angular/platform-browser-dynamic": "~13.2.0",
"@angular/router": "~13.2.0",
"@ngrx/effects": "^13.1.0",
"@ngrx/store": "^13.1.0",
"@ngrx/store-devtools": "^13.1.0",
"bootstrap": "^5.1.3",
"concurrently": "^7.0.0",
"font-awesome": "^4.7.0",

View File

@@ -1,10 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EmployeesComponent } from './components/employees/employees.component';
const routes: Routes = [];
const routes: Routes = [{
path:"employees", component:EmployeesComponent
}];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -5,7 +5,7 @@
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Link 1</a>
<a class="nav-link" routerLink="/employees">Employees</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 2</a>

View File

@@ -1,19 +1,30 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EmployeesComponent } from './components/employees/employees.component';
import { NavBarComponent } from './components/employees/nav-bar/nav-bar.component';
import { employeesReducer } from './ngrx/employees.reducer';
import { EmployeesEffects } from './ngrx/employees.effects';
@NgModule({
declarations: [
AppComponent
AppComponent,
EmployeesComponent,
NavBarComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
HttpClientModule,
StoreModule.forRoot({catalogState:employeesReducer}),
EffectsModule.forRoot([EmployeesEffects]),
StoreDevtoolsModule.instrument()
],
providers: [],
bootstrap: [AppComponent]

View File

@@ -0,0 +1,27 @@
<div class="p-4">
<app-nav-bar></app-nav-bar>
<ng-container *ngIf="employeesState$ | async as state" [ngSwitch]="state.dataState">
<ng-container *ngSwitchCase="EmployeesStateEnum.INITIAL">
<div class="p-2">Initial State</div>
</ng-container>
<ng-container *ngSwitchCase="EmployeesStateEnum.LOADING">
<div class="p-2">Loading ...</div>
</ng-container>
<ng-container *ngSwitchCase="EmployeesStateEnum.ERROR">
<div class="p-2">{{state.errorMessage | json}}</div>
</ng-container>
<ng-container *ngSwitchCase="EmployeesStateEnum.LOADED" class="p-4">
<table class="table">
<tr>
<th>ID</th><th>Nom</th><th>Prénom</th><th>Role</th>
</tr>
<tr *ngFor="let employee of state.employees">
<td>{{employee.id}}</td>
<td>{{employee.nom}}</td>
<td>{{employee.prenom}}</td>
<td>{{employee.role}}</td>
</tr>
</table>
</ng-container>
</ng-container>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EmployeesComponent } from './employees.component';
describe('EmployeesComponent', () => {
let component: EmployeesComponent;
let fixture: ComponentFixture<EmployeesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EmployeesComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(EmployeesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,24 @@
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { EmployeesState, EmployeesStateEnum } from '../../ngrx/employees.reducer';
@Component({
selector: 'app-employees',
templateUrl: './employees.component.html',
styleUrls: ['./employees.component.css']
})
export class EmployeesComponent implements OnInit {
employeesState$:Observable<EmployeesState>|null=null;
readonly EmployeesStateEnum=EmployeesStateEnum;
constructor(private store:Store<any>) { }
ngOnInit(): void {
this.employeesState$=this.store.pipe(
map((state) => state.catalogState)
);
}
}

View File

@@ -0,0 +1,5 @@
<ul class="nav nav-pills">
<li>
<button class="btn btn-outline-info" (click)="onGetAllEmployees()">All</button>
</li>
</ul>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NavBarComponent } from './nav-bar.component';
describe('NavBarComponent', () => {
let component: NavBarComponent;
let fixture: ComponentFixture<NavBarComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NavBarComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NavBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,20 @@
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { GetAllEmployeesAction } from '../../../ngrx/employees.actions';
@Component({
selector: 'app-nav-bar',
templateUrl: './nav-bar.component.html',
styleUrls: ['./nav-bar.component.css']
})
export class NavBarComponent implements OnInit {
constructor(private store:Store<any>) { }
ngOnInit(): void {
}
onGetAllEmployees() {
this.store.dispatch(new GetAllEmployeesAction({}))
}
}

View File

@@ -0,0 +1,31 @@
import { Action } from '@ngrx/store';
import { Employee } from '../model/employee.model';
export enum EmployeesActionsTypes {
GET_ALL_EMPLOYEES="[Employees] Get All Employees",
GET_ALL_EMPLOYEES_SUCCESS="[Employees] Get All Employees Success",
GET_ALL_EMPLOYEES_ERROR="[Employees] Get All Employees Error",
}
export class GetAllEmployeesAction implements Action {
type: EmployeesActionsTypes=EmployeesActionsTypes.GET_ALL_EMPLOYEES;
constructor(public payload:any) {
}
}
export class GetAllEmployeesActionSuccess implements Action {
type: EmployeesActionsTypes=EmployeesActionsTypes.GET_ALL_EMPLOYEES_SUCCESS;
constructor(public payload:Employee[]) {
}
}
export class GetAllEmployeesActionError implements Action {
type: EmployeesActionsTypes=EmployeesActionsTypes.GET_ALL_EMPLOYEES_ERROR;
constructor(public payload:string) {
}
}
export type EmployeesActions=GetAllEmployeesAction | GetAllEmployeesActionSuccess | GetAllEmployeesActionError;

View File

@@ -0,0 +1,26 @@
import { Injectable } from '@angular/core';
import { EmployeesService } from '../services/employees.service';
import { createEffect, Actions, ofType } from '@ngrx/effects';
import { Observable, of, EMPTY } from 'rxjs';
import { Action } from '@ngrx/store';
import { EmployeesActionsTypes } from './employees.actions';
import { mergeMap, map, catchError } from 'rxjs/operators';
import { GetAllEmployeesActionSuccess, GetAllEmployeesActionError } from './employees.actions';
@Injectable()
export class EmployeesEffects {
getAllEmployeesEffect$:Observable<Action> = createEffect(() => this.actions$.pipe(
ofType(EmployeesActionsTypes.GET_ALL_EMPLOYEES),
mergeMap((action) => {
return this.employeesService.getAllEmployees().pipe(
map(employees => new GetAllEmployeesActionSuccess(employees)),
catchError((err) => of(new GetAllEmployeesActionError(err.message)))
)
})
));
constructor(
private employeesService:EmployeesService,
private actions$:Actions
) {}
}

View File

@@ -0,0 +1,35 @@
import { Employee } from '../model/employee.model';
import { EmployeesActions, EmployeesActionsTypes } from './employees.actions';
import { Action } from '@ngrx/store';
export enum EmployeesStateEnum {
LOADING="Loading",
LOADED="Loaded",
ERROR="Error",
INITIAL="Initial"
}
export interface EmployeesState {
employees:Employee[],
errorMessage:string,
dataState:EmployeesStateEnum
}
const initState:EmployeesState = {
employees:[],
errorMessage:"",
dataState:EmployeesStateEnum.INITIAL
}
export function employeesReducer(state:EmployeesState=initState, action:Action):EmployeesState {
switch(action.type) {
case EmployeesActionsTypes.GET_ALL_EMPLOYEES:
return {...state, dataState:EmployeesStateEnum.LOADING};
case EmployeesActionsTypes.GET_ALL_EMPLOYEES_SUCCESS:
return {...state, dataState:EmployeesStateEnum.LOADED, employees:(<EmployeesActions>action).payload};
case EmployeesActionsTypes.GET_ALL_EMPLOYEES_ERROR:
return {...state, dataState:EmployeesStateEnum.ERROR, errorMessage:(<EmployeesActions>action).payload};
default:
return {...state}
}
}

View File

@@ -9,7 +9,7 @@ export class EmployeesService {
constructor(private http:HttpClient) {}
getAllEmployees():Observable<Employee[]> {
let host=environment.host;
let host=Math.random()>0.2?environment.host:environment.unreachableHost;
return this.http.get<Employee[]>(host+"/employees");
}