Ajout d'un exemple pour les appels http.

This commit is contained in:
Florian THIERRY
2023-02-21 15:25:03 +01:00
parent 4002d4226a
commit cfd4838d17
18 changed files with 243 additions and 13 deletions

22
package-lock.json generated
View File

@@ -18,6 +18,7 @@
"@angular/platform-browser": "^15.1.0", "@angular/platform-browser": "^15.1.0",
"@angular/platform-browser-dynamic": "^15.1.0", "@angular/platform-browser-dynamic": "^15.1.0",
"@angular/router": "^15.1.0", "@angular/router": "^15.1.0",
"angular-in-memory-web-api": "^0.15.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.12.0" "zone.js": "~0.12.0"
@@ -4382,6 +4383,19 @@
"ajv": "^8.8.2" "ajv": "^8.8.2"
} }
}, },
"node_modules/angular-in-memory-web-api": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.15.0.tgz",
"integrity": "sha512-T07vQTeBEGWfwZRM+jIooYgK4c8LR25I27otIthUkFINAREoPiBNogWDxlnROIeu1k2RrY3QJjMOYq7f62UcgA==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^15.0.0",
"@angular/core": "^15.0.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/ansi-colors": { "node_modules/ansi-colors": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@@ -15346,6 +15360,14 @@
"fast-deep-equal": "^3.1.3" "fast-deep-equal": "^3.1.3"
} }
}, },
"angular-in-memory-web-api": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.15.0.tgz",
"integrity": "sha512-T07vQTeBEGWfwZRM+jIooYgK4c8LR25I27otIthUkFINAREoPiBNogWDxlnROIeu1k2RrY3QJjMOYq7f62UcgA==",
"requires": {
"tslib": "^2.3.0"
}
},
"ansi-colors": { "ansi-colors": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",

View File

@@ -20,6 +20,7 @@
"@angular/platform-browser": "^15.1.0", "@angular/platform-browser": "^15.1.0",
"@angular/platform-browser-dynamic": "^15.1.0", "@angular/platform-browser-dynamic": "^15.1.0",
"@angular/router": "^15.1.0", "@angular/router": "^15.1.0",
"angular-in-memory-web-api": "^0.15.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.12.0" "zone.js": "~0.12.0"

View File

@@ -4,12 +4,17 @@ import {PromisesExampleComponent} from "./promises-example/promises-example.comp
import {HomeComponent} from "./home/home.component"; import {HomeComponent} from "./home/home.component";
import {ObservablesExampleComponent} from "./observables-example/observables-example.component"; import {ObservablesExampleComponent} from "./observables-example/observables-example.component";
import {StateManagementExampleComponent} from "./state-management-example/state-management-example.component"; import {StateManagementExampleComponent} from "./state-management-example/state-management-example.component";
import {NetworkCallComponent} from "./network-call/network-call.component";
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
component: HomeComponent component: HomeComponent
}, },
{
path: 'network-call',
component: NetworkCallComponent
},
{ {
path: 'promises', path: 'promises',
component: PromisesExampleComponent component: PromisesExampleComponent

View File

@@ -12,9 +12,11 @@ import {BackToHomeComponent} from './back-to-home/back-to-home.component';
import {MatIconModule} from "@angular/material/icon"; import {MatIconModule} from "@angular/material/icon";
import {StateManagementExampleComponent} from './state-management-example/state-management-example.component'; import {StateManagementExampleComponent} from './state-management-example/state-management-example.component';
import {RestServicesModule} from "./core/rest-services/rest-services.module"; import {RestServicesModule} from "./core/rest-services/rest-services.module";
import {CarComponent} from './core/components/car/car.component';
import {ObservablesExampleModule} from "./observables-example/observables-example.module"; import {ObservablesExampleModule} from "./observables-example/observables-example.module";
import {CoreModule} from "./core/core.module"; import {CoreModule} from "./core/core.module";
import {NetworkCallModule} from "./network-call/network-call.module";
import {HttpClientInMemoryWebApiModule} from "angular-in-memory-web-api";
import {RestApiMockService} from "./core/rest-api-mock/rest-api-mock.service";
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -33,7 +35,14 @@ import {CoreModule} from "./core/core.module";
MatButtonModule, MatButtonModule,
MatIconModule, MatIconModule,
RestServicesModule, RestServicesModule,
ObservablesExampleModule ObservablesExampleModule,
NetworkCallModule,
HttpClientInMemoryWebApiModule.forRoot(
RestApiMockService,
{
dataEncapsulation: false
}
)
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@@ -3,7 +3,8 @@ import {CommonModule} from '@angular/common';
import {CarComponent} from "./components/car/car.component"; import {CarComponent} from "./components/car/car.component";
import {RestServicesModule} from "./rest-services/rest-services.module"; import {RestServicesModule} from "./rest-services/rest-services.module";
import {MaterialModule} from "./material.module"; import {MaterialModule} from "./material.module";
import {HttpClientInMemoryWebApiModule} from "angular-in-memory-web-api";
import {RestApiMockService} from "./rest-api-mock/rest-api-mock.service";
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -12,13 +13,13 @@ import {MaterialModule} from "./material.module";
imports: [ imports: [
CommonModule, CommonModule,
RestServicesModule, RestServicesModule,
MaterialModule MaterialModule,
], ],
exports: [ exports: [
CarComponent, CarComponent,
RestServicesModule, RestServicesModule,
MaterialModule MaterialModule
] ],
}) })
export class CoreModule { export class CoreModule {
} }

View File

@@ -2,11 +2,15 @@ import {NgModule} from "@angular/core";
import {MatIconModule} from "@angular/material/icon"; import {MatIconModule} from "@angular/material/icon";
import {MatButtonModule} from "@angular/material/button"; import {MatButtonModule} from "@angular/material/button";
import {MatTooltipModule} from "@angular/material/tooltip"; import {MatTooltipModule} from "@angular/material/tooltip";
import {MatSnackBar, MatSnackBarModule} from "@angular/material/snack-bar";
import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
const IMPORTED_MATERIAL_MODULES = [ const IMPORTED_MATERIAL_MODULES = [
MatIconModule,
MatButtonModule, MatButtonModule,
MatTooltipModule MatIconModule,
MatProgressSpinnerModule,
MatSnackBarModule,
MatTooltipModule,
] ]
@NgModule({ @NgModule({

View File

@@ -1,5 +1,5 @@
export interface Car { export interface Car {
id: string; id: number;
brand: string; brand: string;
model: string; model: string;
power: number; power: number;

View File

@@ -0,0 +1,37 @@
import {Injectable} from '@angular/core';
import {InMemoryDbService} from "angular-in-memory-web-api";
import {Car} from "../model/car";
@Injectable({
providedIn: 'root'
})
export class RestApiMockService implements InMemoryDbService {
createDb() {
const cars: Car[] = [
{
id: 1,
brand: 'Toyota',
model: 'Yaris',
power: 12,
numberOfSeats: 5
},
{
id: 2,
brand: 'Citroën',
model: 'DS',
power: 14,
numberOfSeats: 5
},
{
id: 3,
brand: 'Renault',
model: 'Twingo',
power: 8,
numberOfSeats: 2
}
];
return {
cars
};
}
}

View File

@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http"; import {HttpClient} from "@angular/common/http";
import {Car} from "../model/car"; import {Car} from "../model/car";
import {toPromise} from "../utils/promises.utils"; import {toPromise} from "../utils/promises.utils";
import {Observable} from "rxjs";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -12,7 +13,11 @@ export class CarRestService {
private http: HttpClient private http: HttpClient
) { } ) { }
findById(carId: string): Promise<Car> { findById(carId: number): Promise<Car> {
return toPromise(this.http.get<Car>(`/cars/${carId}`)); return toPromise(this.findById$(carId));
}
findById$(carId: number): Observable<Car> {
return this.http.get<Car>(`/api/cars/${carId}`);
} }
} }

View File

@@ -8,6 +8,10 @@ export interface Link {
} }
const LINKS: Link[] = [{ const LINKS: Link[] = [{
label: 'Les appels réseaux - Promesses ou Observables ?',
icon: '🌍',
href: '/network-call'
}, {
label: 'Les promesses', label: 'Les promesses',
icon: '🙏', icon: '🙏',
href: '/promises' href: '/promises'

View File

@@ -0,0 +1,20 @@
<div class="component">
<h1>Les appels réseaux - Promesses ou Observables ?</h1>
<div class="actions">
<button mat-raised-button
(click)="loadCar()"
[disabled]="isLoading$ | async">
Load the car
</button>
</div>
<div class="loading" *ngIf="isLoading$ | async">
<h2>Loading...</h2>
<mat-spinner></mat-spinner>
</div>
<ng-container *ngIf="isLoaded">
<h2>Car data</h2>
<div class="centered">
<app-car [value]="car"></app-car>
</div>
</ng-container>
</div>

View File

@@ -0,0 +1,7 @@
.component {
.loading {
display: flex;
flex-direction: column;
align-items: center;
}
}

View File

@@ -0,0 +1,76 @@
import {Component} from '@angular/core';
import {BehaviorSubject, catchError, EMPTY, finalize, Observable, tap} from "rxjs";
import {Car} from "../core/model/car";
import {CarRestService} from "../core/rest-services/car.rest-service";
import {MatSnackBar} from "@angular/material/snack-bar";
const CAR_ID = 1;
@Component({
selector: 'app-network-call',
templateUrl: './network-call.component.html',
styleUrls: ['./network-call.component.scss']
})
export class NetworkCallComponent {
private isLoadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
isLoaded: boolean = false;
car?: Car;
constructor(
private carRestService: CarRestService,
private snackBar: MatSnackBar
) {
}
get isLoading$(): Observable<boolean> {
return this.isLoadingSubject.asObservable();
}
loadCar(): void {
this.loadThroughAPromise();
// this.loadThroughAnObservable();
}
private loadThroughAPromise(): void {
this.isLoadingSubject.next(true);
this.isLoaded = false;
this.carRestService.findById(CAR_ID)
.then(car => {
this.car = car;
this.isLoaded = true;
})
.catch(error => {
console.log('An error occured while loading the car.', error);
this.snackBar.open(
'An error occured while loading the car.',
'Close',
{duration: 2000}
);
})
.finally(() => this.isLoadingSubject.next(false));
}
private loadThroughAnObservable(): void {
this.isLoadingSubject.next(true);
this.isLoaded = false;
this.carRestService.findById$(CAR_ID)
.pipe(
tap(car => {
this.car = car;
this.isLoaded = true;
}),
catchError(error => {
console.log('An error occured while loading the car.', error);
this.snackBar.open(
'An error occured while loading the car.',
'Close',
{duration: 2000}
);
return EMPTY;
}),
finalize(() => this.isLoadingSubject.next(false))
).subscribe();
}
}

View File

@@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NetworkCallComponent} from "./network-call.component";
import {CoreModule} from "../core/core.module";
import {HttpClientModule} from "@angular/common/http";
@NgModule({
declarations: [
NetworkCallComponent
],
imports: [
CommonModule,
CoreModule,
HttpClientModule
],
exports: [
NetworkCallComponent
]
})
export class NetworkCallModule {
}

View File

@@ -6,5 +6,10 @@ import { Component } from '@angular/core';
styleUrls: ['./observables-example.component.scss'] styleUrls: ['./observables-example.component.scss']
}) })
export class ObservablesExampleComponent { export class ObservablesExampleComponent {
/*
Formulaires :
-> valueChanges / debounceTime etc...
Appel réseau -> pour voir la diff avec les promesses
*/
} }

View File

@@ -17,7 +17,7 @@ export class SimpleObservableExempleComponent {
addNewCar(): void { addNewCar(): void {
const newCar: Car = { const newCar: Car = {
id: `${Math.random() * 100}`, id: Math.random() * 100,
brand: 'Toyota', brand: 'Toyota',
model: 'Yaris', model: 'Yaris',
power: 12, power: 12,

View File

@@ -8,10 +8,17 @@ import {Car} from "../core/model/car";
}) })
export class PromisesExampleComponent { export class PromisesExampleComponent {
car?: Car = { car?: Car = {
id: '1234567890', id: 1,
brand: 'Toyota', brand: 'Toyota',
model: 'Yaris', model: 'Yaris',
power: 12, power: 12,
numberOfSeats: 5 numberOfSeats: 5
}; };
/*
// Async / Await -> + comparaison avec les then
// new Promise "à la main"
// les then chaînés
// catch (avec 1 ou plusieurs then) -> try/catch avec un await
*/
} }

View File

@@ -2,3 +2,9 @@
html, body { height: 100%; } html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
.centered {
display: flex;
flex-direction: row;
justify-content: center;
}