Cours n°4: Découpage en sous-composant

This commit is contained in:
Vincent BENOIT
2022-03-24 16:28:20 +01:00
parent f409540461
commit 12deab061d
12 changed files with 250 additions and 94 deletions

View File

@@ -10,6 +10,8 @@ import { EmployeesComponent } from './components/employees/employees.component';
import { HomeComponent } from './components/home/home.component';
import { EmployeeAddComponent } from './components/employee-add/employee-add.component';
import { EmployeeEditComponent } from './components/employee-edit/employee-edit.component';
import { EmployeesNavbarComponent } from './components/employees/employees-navbar/employees-navbar.component';
import { EmployeesListComponent } from './components/employees/employees-list/employees-list.component';
@NgModule({
declarations: [
@@ -18,7 +20,9 @@ import { EmployeeEditComponent } from './components/employee-edit/employee-edit.
EmployeesComponent,
HomeComponent,
EmployeeAddComponent,
EmployeeEditComponent
EmployeeEditComponent,
EmployeesNavbarComponent,
EmployeesListComponent
],
imports: [
BrowserModule,

View File

@@ -0,0 +1,39 @@
<div class="container">
<ng-container *ngIf="(employeesInput$ | async) as result" [ngSwitch]="result.dataState">
<ng-container *ngSwitchCase="DataStateEnum.LOADING">
Loading ...
</ng-container>
<ng-container *ngSwitchCase="DataStateEnum.ERROR">
<div class="alert-danger">
{{result.errorMessage}}
</div>
</ng-container>
<ng-container *ngSwitchCase="DataStateEnum.LOADED">
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>ID</th><th>Nom</th><th>Prenom</th><th>Role</th><th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of result.data">
<td>{{p.id}}</td>
<td>{{p.nom}}</td>
<td>{{p.prenom}}</td>
<td>{{p.role}}</td>
<td>
<button type="button" class="btn btn-outline-danger" (click)="onDelete(p)">
<span class="fa fa-trash"></span>
</button>
</td>
<td>
<button type="button" class="btn btn-outline-info" (click)="onEdit(p)">
<span class="fa fa-edit"></span>
</button>
</td>
</tr>
</tbody>
</table>
</ng-container>
</ng-container>
</div>

View File

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

View File

@@ -0,0 +1,32 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { AppDataState, DataStateEnum, EmployeeActionsTypes, ActEvent } from '../../../state/employee.state';
import { Employee } from '../../../model/employee.model';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-employees-list',
templateUrl: './employees-list.component.html',
styleUrls: ['./employees-list.component.css']
})
export class EmployeesListComponent implements OnInit {
@Input() employeesInput$:Observable<AppDataState<Employee[]>>|null=null;
@Output() evtEmit:EventEmitter<ActEvent>=new EventEmitter();
readonly DataStateEnum=DataStateEnum;
constructor() { }
ngOnInit(): void {
}
onDelete(val:Employee) {
this.evtEmit.emit({
type:EmployeeActionsTypes.DELETE_EMPLOYEE, payload:val
});
}
onEdit(val:Employee) {
this.evtEmit.emit({
type:EmployeeActionsTypes.EDIT_EMPLOYEE, payload:val
});
}
}

View File

@@ -0,0 +1,18 @@
<nav class="navbar navbar-expand-sm navbar-light bg-light">
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<button (click)="onGetAllEmployees()" class="btn btn-sm btn-outline-info ml-2">All</button>
</li>
<li class="nav-item">
<button (click)="onNewEmployee()" class="btn btn-sm btn-outline-info ml-2">Ajout</button>
</li>
<form #f="ngForm" (ngSubmit)="onSearch(f.value)" class="form-inline my-2 my-lg-0">
<input ngModel class="mr-sm-2" name="keyword" type="text">
<button class="btn btn-sm btn-outline-info my-2 my-sm-0">
<span class="fa fa-search"></span>
</button>
</form>
</ul>
</div>
</nav>

View File

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

View File

@@ -0,0 +1,28 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { EmployeeActionsTypes, ActEvent } from '../../../state/employee.state';
@Component({
selector: 'app-employees-navbar',
templateUrl: './employees-navbar.component.html',
styleUrls: ['./employees-navbar.component.css']
})
export class EmployeesNavbarComponent implements OnInit {
@Output() evtEmit:EventEmitter<ActEvent>=new EventEmitter();
constructor() { }
ngOnInit(): void {
}
onGetAllEmployees() {
this.evtEmit.emit({type:EmployeeActionsTypes.GET_ALL_EMPLOYEES});
}
onSearch(val:any) {
this.evtEmit.emit({type:EmployeeActionsTypes.SEARCH_EMPLOYEES, payload:val});
}
onNewEmployee() {
this.evtEmit.emit({type:EmployeeActionsTypes.NEW_EMPLOYEE});
}
}

View File

@@ -1,57 +1,2 @@
<nav class="navbar navbar-expand-sm navbar-light bg-light">
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<button (click)="onGetAllEmployees()" class="btn btn-sm btn-outline-info ml-2">All</button>
</li>
<li class="nav-item">
<button (click)="onNewEmployee()" class="btn btn-sm btn-outline-info ml-2">Ajout</button>
</li>
<form #f="ngForm" (ngSubmit)="onSearch(f.value)" class="form-inline my-2 my-lg-0">
<input ngModel class="mr-sm-2" name="keyword" type="text">
<button class="btn btn-sm btn-outline-info my-2 my-sm-0">
<span class="fa fa-search"></span>
</button>
</form>
</ul>
</div>
</nav>
<div class="container">
<ng-container *ngIf="(employees$ | async) as result" [ngSwitch]="result.dataState">
<ng-container *ngSwitchCase="DataStateEnum.LOADING">
Loading ...
</ng-container>
<ng-container *ngSwitchCase="DataStateEnum.ERROR">
<div class="alert-danger">
{{result.errorMessage}}
</div>
</ng-container>
<ng-container *ngSwitchCase="DataStateEnum.LOADED">
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>ID</th><th>Nom</th><th>Prenom</th><th>Role</th><th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of result.data">
<td>{{p.id}}</td>
<td>{{p.nom}}</td>
<td>{{p.prenom}}</td>
<td>{{p.role}}</td>
<td>
<button type="button" class="btn btn-outline-danger" (click)="onDelete(p)">
<span class="fa fa-trash"></span>
</button>
</td>
<td>
<button type="button" class="btn btn-outline-info" (click)="onEdit(p)">
<span class="fa fa-edit"></span>
</button>
</td>
</tr>
</tbody>
</table>
</ng-container>
</ng-container>
</div>
<app-employees-navbar (evtEmit)="onActionEvent($event)"></app-employees-navbar>
<app-employees-list [employeesInput$]="employees$" (evtEmit)="onActionEvent($event)"></app-employees-list>

View File

@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { EmployeesService } from '../../services/employees.service';
import { Employee } from '../../model/employee.model';
import { AppDataState, DataStateEnum } from '../../state/employee.state';
import { AppDataState, DataStateEnum, EmployeeActionsTypes, ActEvent } from '../../state/employee.state';
import { Observable, of } from 'rxjs';
import { catchError, map, startWith } from 'rxjs/operators';
import { Router } from '@angular/router';
@@ -12,46 +12,73 @@ import { Router } from '@angular/router';
styleUrls: ['./employees.component.css']
})
export class EmployeesComponent implements OnInit {
employees$:Observable<AppDataState<Employee[]>>|null=null;
readonly DataStateEnum=DataStateEnum;
employees$:Observable<AppDataState<Employee[]>>|null=null;
readonly DataStateEnum=DataStateEnum;
constructor(private employeesService:EmployeesService, private router:Router) { }
constructor(private employeesService:EmployeesService, private router:Router) { }
ngOnInit(): void {
}
ngOnInit(): void { }
onGetAllEmployees() {
this.employees$=this.employeesService.getAllEmployees().pipe(
map(data=>{
return ({dataState:DataStateEnum.LOADED, data:data})
}),
startWith({dataState:DataStateEnum.LOADING}),
catchError(err=>of({dataState: DataStateEnum.ERROR, errorMessage:err.message}))
);
}
onActionEvent($event: ActEvent) {
switch($event.type) {
case EmployeeActionsTypes.GET_ALL_EMPLOYEES: {
this.onGetAllEmployees();
break;
}
case EmployeeActionsTypes.SEARCH_EMPLOYEES: {
this.onSearch($event.payload);
break;
}
case EmployeeActionsTypes.NEW_EMPLOYEE: {
this.onNewEmployee();
break;
}
case EmployeeActionsTypes.EDIT_EMPLOYEE: {
this.onEdit($event.payload);
break;
}
case EmployeeActionsTypes.DELETE_EMPLOYEE: {
this.onDelete($event.payload);
break;
}
default: {
break;
}
}
}
onSearch(val:any) {
this.employees$=this.employeesService.searchEmployees(val.keyword).pipe(
map(data=>{
return ({dataState:DataStateEnum.LOADED, data:data})
}),
startWith({dataState:DataStateEnum.LOADING}),
catchError(err=>of({dataState: DataStateEnum.ERROR, errorMessage:err.message}))
);
}
onGetAllEmployees() {
this.employees$=this.employeesService.getAllEmployees().pipe(
map(data=>{
return ({dataState:DataStateEnum.LOADED, data:data})
}),
startWith({dataState:DataStateEnum.LOADING}),
catchError(err=>of({dataState: DataStateEnum.ERROR, errorMessage:err.message}))
);
}
onDelete(val:Employee) {
this.employeesService.deleteEmployee(val).subscribe(
data=>{
this.onGetAllEmployees();
});
}
onSearch(val:any) {
this.employees$=this.employeesService.searchEmployees(val.keyword).pipe(
map(data=>{
return ({dataState:DataStateEnum.LOADED, data:data})
}),
startWith({dataState:DataStateEnum.LOADING}),
catchError(err=>of({dataState: DataStateEnum.ERROR, errorMessage:err.message}))
);
}
onNewEmployee() {
this.router.navigateByUrl("/newEmployee");
}
onDelete(val:Employee) {
this.employeesService.deleteEmployee(val).subscribe(
data=>{
this.onGetAllEmployees();
});
}
onEdit(val:Employee) {
this.router.navigateByUrl("/editEmployee/"+val.id);
}
onNewEmployee() {
this.router.navigateByUrl("/newEmployee");
}
onEdit(val:Employee) {
this.router.navigateByUrl("/editEmployee/"+val.id);
}
}

View File

@@ -1,3 +1,16 @@
export enum EmployeeActionsTypes {
GET_ALL_EMPLOYEES="[Employees] Get all employees",
SEARCH_EMPLOYEES="[Employees] Search employees",
NEW_EMPLOYEE="[Employees] New employee",
EDIT_EMPLOYEE="[Employees] Edit employee",
DELETE_EMPLOYEE="[Employees] Remove employee"
}
export interface ActEvent {
type:EmployeeActionsTypes,
payload?:any
}
export enum DataStateEnum {
LOADING,
LOADED,