Fin section 14 et 15 : programmation réactive et requêtes HTTP
This commit is contained in:
parent
a789a10203
commit
108c2b17a7
@ -25,6 +25,13 @@
|
||||
"@angular-devkit/build-angular": "^15.1.5",
|
||||
"@angular/cli": "~15.1.5",
|
||||
"@angular/compiler-cli": "^15.1.0",
|
||||
"angular-in-memory-web-api": "^0.15.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"typescript": "~4.9.4"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,27 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
|
||||
import { PokemonModule } from './pokemon/pokemon.module';
|
||||
import { InMemoryDataService } from './in-memory-data.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, PageNotFoundComponent],
|
||||
imports: [BrowserModule, FormsModule, PokemonModule, AppRoutingModule],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {
|
||||
dataEncapsulation: false,
|
||||
}),
|
||||
PokemonModule,
|
||||
AppRoutingModule,
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
15
src/app/in-memory-data.service.ts
Normal file
15
src/app/in-memory-data.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||
import { POKEMONS } from './pokemon/mock-pokemon-list';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InMemoryDataService implements InMemoryDbService {
|
||||
createDb() {
|
||||
const pokemons = POKEMONS;
|
||||
return { pokemons };
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
}
|
2
src/app/pokemon/add-pokemon/add-pokemon.component.html
Normal file
2
src/app/pokemon/add-pokemon/add-pokemon.component.html
Normal file
@ -0,0 +1,2 @@
|
||||
<h2 class="center">Ajouter un pokémon</h2>
|
||||
<app-pokemon-form [pokemon]="pokemon"></app-pokemon-form>
|
15
src/app/pokemon/add-pokemon/add-pokemon.component.ts
Normal file
15
src/app/pokemon/add-pokemon/add-pokemon.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Pokemon } from '../pokemon';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-pokemon',
|
||||
templateUrl: './add-pokemon.component.html',
|
||||
styles: [],
|
||||
})
|
||||
export class AddPokemonComponent implements OnInit {
|
||||
pokemon: Pokemon;
|
||||
|
||||
ngOnInit() {
|
||||
this.pokemon = new Pokemon();
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@
|
||||
<div class="card-action">
|
||||
<a (click)="goToPokemonList()">Retour</a>
|
||||
<a (click)="goToEditPokemon(pokemon)">Modifier</a>
|
||||
<a (click)="deletePokemon(pokemon)">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,10 +19,18 @@ export class DetailPokemonComponent implements OnInit {
|
||||
ngOnInit(): void {
|
||||
const pokemonId: string | null = this.route.snapshot.paramMap.get('id');
|
||||
if (pokemonId) {
|
||||
this.pokemon = this.pokemonService.getPokemonById(+pokemonId);
|
||||
this.pokemonService
|
||||
.getPokemonById(+pokemonId)
|
||||
.subscribe((pokemon) => (this.pokemon = pokemon));
|
||||
}
|
||||
}
|
||||
|
||||
deletePokemon(pokemon: Pokemon) {
|
||||
this.pokemonService
|
||||
.deletePokemonById(pokemon)
|
||||
.subscribe(() => this.goToPokemonList());
|
||||
}
|
||||
|
||||
goToPokemonList() {
|
||||
this.router.navigate(['/pokemons']);
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ export class EditPokemonComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
const pokemonId: string | null = this.route.snapshot.paramMap.get('id');
|
||||
if (pokemonId) {
|
||||
this.pokemon = this.pokemonService.getPokemonById(+pokemonId);
|
||||
this.pokemonService
|
||||
.getPokemonById(+pokemonId)
|
||||
.subscribe((pokemon) => (this.pokemon = pokemon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,3 +29,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
class="btn-floating btn-large waves-effect waves-ligth red z-depth-3"
|
||||
style="position: fixed; bottom: 25px; right: 25px"
|
||||
routerLink="/pokemon/add"
|
||||
>➕</a
|
||||
>
|
||||
|
@ -13,7 +13,9 @@ export class ListPokemonComponent implements OnInit {
|
||||
constructor(private router: Router, private pokemonService: PokemonService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.pokemonList = this.pokemonService.getPokemonList();
|
||||
this.pokemonService
|
||||
.getPokemonList()
|
||||
.subscribe((pokemonList) => (this.pokemonList = pokemonList));
|
||||
}
|
||||
|
||||
goToPokemon(pokemon: Pokemon) {
|
||||
|
@ -24,6 +24,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pokemon picture -->
|
||||
<div *ngIf="isAddForm" class="form-group">
|
||||
<label for="name">Image</label>
|
||||
<input
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="picture"
|
||||
required
|
||||
[(ngModel)]="pokemon.picture"
|
||||
name="picture"
|
||||
#picture="ngModel"
|
||||
/>
|
||||
|
||||
<div
|
||||
[hidden]="picture.valid || picture.pristine"
|
||||
class="card-panel red accent-1"
|
||||
>
|
||||
L’image du pokémon est requise.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pokemon hp -->
|
||||
<div class="form-group">
|
||||
<label for="hp">Point de vie</label>
|
||||
|
@ -11,11 +11,15 @@ import { PokemonService } from '../pokemon.service';
|
||||
export class PokemonFormComponent implements OnInit {
|
||||
@Input() pokemon: Pokemon;
|
||||
types: string[];
|
||||
isAddForm: boolean;
|
||||
|
||||
constructor(private pokemonService: PokemonService, private router: Router) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.types = this.pokemonService.getPokemonTypeList();
|
||||
this.pokemonService
|
||||
.getPokemonTypeList()
|
||||
.subscribe((types) => (this.types = types));
|
||||
this.isAddForm = this.router.url.includes('add');
|
||||
}
|
||||
|
||||
hasType(type: string) {
|
||||
@ -33,8 +37,17 @@ export class PokemonFormComponent implements OnInit {
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
console.log('Submit form!');
|
||||
this.router.navigate(['/pokemon', this.pokemon.id]);
|
||||
if (this.isAddForm) {
|
||||
this.pokemonService
|
||||
.addPokemon(this.pokemon)
|
||||
.subscribe((pokemon: Pokemon) => {
|
||||
this.router.navigate(['/pokemon', pokemon.id]);
|
||||
});
|
||||
} else {
|
||||
this.pokemonService.updatePokemon(this.pokemon).subscribe(() => {
|
||||
this.router.navigate(['/pokemon', this.pokemon.id]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isTypesValid(type: string): boolean {
|
||||
|
@ -10,9 +10,11 @@ import { PokemonService } from './pokemon.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { PokemonFormComponent } from './pokemon-form/pokemon-form.component';
|
||||
import { EditPokemonComponent } from './edit-pokemon/edit-pokemon.component';
|
||||
import { AddPokemonComponent } from './add-pokemon/add-pokemon.component';
|
||||
|
||||
const pokemonRoutes: Routes = [
|
||||
{ path: 'edit/pokemon/:id', component: EditPokemonComponent },
|
||||
{ path: 'pokemon/add', component: AddPokemonComponent },
|
||||
{ path: 'pokemon/:id', component: DetailPokemonComponent },
|
||||
{ path: 'pokemons', component: ListPokemonComponent },
|
||||
];
|
||||
@ -25,6 +27,7 @@ const pokemonRoutes: Routes = [
|
||||
DetailPokemonComponent,
|
||||
PokemonFormComponent,
|
||||
EditPokemonComponent,
|
||||
AddPokemonComponent,
|
||||
],
|
||||
imports: [CommonModule, FormsModule, RouterModule.forChild(pokemonRoutes)],
|
||||
providers: [PokemonService],
|
||||
|
@ -1,22 +1,74 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { POKEMONS } from './mock-pokemon-list';
|
||||
import { catchError, mergeMap, Observable, of, tap } from 'rxjs';
|
||||
import { Pokemon } from './pokemon';
|
||||
|
||||
@Injectable()
|
||||
export class PokemonService {
|
||||
getPokemonList(): Pokemon[] {
|
||||
return POKEMONS;
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getPokemonList(): Observable<Pokemon[]> {
|
||||
return this.http.get<Pokemon[]>('api/pokemons').pipe(
|
||||
tap((pokemonList) => this.log(pokemonList)),
|
||||
catchError((error) => this.handleError(error, []))
|
||||
);
|
||||
}
|
||||
|
||||
getPokemonById(pokemonId: number): Pokemon | undefined {
|
||||
return POKEMONS.find((pokemon) => pokemon.id === pokemonId);
|
||||
getPokemonById(pokemonId: number): Observable<Pokemon | undefined> {
|
||||
return this.http.get<Pokemon>(`api/pokemons/${pokemonId}`).pipe(
|
||||
tap((pokemon) => this.log(pokemon)),
|
||||
catchError((error) => this.handleError(error, undefined))
|
||||
);
|
||||
}
|
||||
|
||||
getPokemonTypeList(): string[] {
|
||||
const types: Set<string> = new Set();
|
||||
POKEMONS.forEach((pokemon) => {
|
||||
pokemon.types.forEach((type) => types.add(type));
|
||||
});
|
||||
return Array.from(types.values());
|
||||
getPokemonTypeList(): Observable<string[]> {
|
||||
return this.http.get<Pokemon[]>('api/pokemons').pipe(
|
||||
mergeMap((pokemons: Pokemon[]) => {
|
||||
const set = new Set<string>();
|
||||
pokemons.forEach((pokemon) =>
|
||||
pokemon.types.forEach((type) => set.add(type))
|
||||
);
|
||||
return [Array.from(set.values())];
|
||||
}),
|
||||
tap((pokemon) => this.log(pokemon)),
|
||||
catchError((error) => this.handleError(error, []))
|
||||
);
|
||||
}
|
||||
|
||||
updatePokemon(pokemon: Pokemon): Observable<null> {
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
|
||||
};
|
||||
return this.http.put('api/pokemons', pokemon, httpOptions).pipe(
|
||||
tap((response) => this.log(response)),
|
||||
catchError((error) => this.handleError(error, null))
|
||||
);
|
||||
}
|
||||
|
||||
addPokemon(pokemon: Pokemon): Observable<Pokemon> {
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
|
||||
};
|
||||
|
||||
return this.http.post<Pokemon>('api/pokemons', pokemon, httpOptions).pipe(
|
||||
tap((response) => this.log(response)),
|
||||
catchError((error) => this.handleError(error, null))
|
||||
);
|
||||
}
|
||||
|
||||
deletePokemonById(pokemon: Pokemon): Observable<null> {
|
||||
return this.http.delete(`api/pokemons/${pokemon.id}`).pipe(
|
||||
tap((response) => this.log(response)),
|
||||
catchError((error) => this.handleError(error, null))
|
||||
);
|
||||
}
|
||||
|
||||
private log(response: any) {
|
||||
console.table(response);
|
||||
}
|
||||
|
||||
private handleError(error: Error, errorValue: any) {
|
||||
console.error(error);
|
||||
return of(errorValue);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,25 @@
|
||||
export class Pokemon {
|
||||
id: number;
|
||||
name: string;
|
||||
hp: number;
|
||||
cp: number;
|
||||
name: string;
|
||||
picture: string;
|
||||
types: Array<string>;
|
||||
created: Date;
|
||||
|
||||
constructor(
|
||||
name: string = 'Enter a name...',
|
||||
hp: number = 100,
|
||||
cp: number = 10,
|
||||
picture: string = 'https://assets.pokemon.com/assets/cms2/img/pokedex/detail/xxx.png',
|
||||
types: string[] = ['Normal'],
|
||||
created: Date = new Date()
|
||||
) {
|
||||
this.hp = hp;
|
||||
this.cp = cp;
|
||||
this.name = name;
|
||||
this.picture = picture;
|
||||
this.types = types;
|
||||
this.created = created;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user