Une fois votre service mis en place, avec @Injectable, nous souhaitons donc récupérer des données json, venant d’une API.
Nous allons donc utiliser HttpClient d’Angular (certain-e-s d’entre vous connaissent son grand frère : le Http).
HttpClient et méthod HTTP
Pour faire une requête REST vers une API pour obtenir des éléments, nous avons le choix de la méthode :
- GET
- POST
- PUT
- DELETE
A votre avis, laquelle utiliser ? GET, vous avez gagné !
Ainsi, ils l’ont bien fait avec HttpClient, vous allez utiliser la méthode : get.
Ce qui donne :
this._httpClient.get(…
Améliorer la désérialisation avec les méthodes génériques
Nous pouvons aider le parser json du HttpClient, en lui précisant le type des données de retour :
this._httpClient.get<Potion[]>(url).
Nous utilisons ici la puissance des méthodes génériques (présentes en C#) et présentes en TypeScript.
Dans notre exemple, nous indiquons à notre HttpClient que le type de retour c’est un tableau de Potion.
Quand le moteur n’agit pas comme le souhaite
Si vous venez d’un autre langage, comme par exemple C#, vous allez bien bien surpris du comportement de HttpClient et de la désérialization effectuée par Angular > TypeScript > javascript.
Création du model
Si vous avez suivi le tutorial Angular, ou bien suivi nos derniers articles, vous avez remarqué que nous avons créé des classes, représentant nos modèles.
En voici un exemple :
export class Potion {
private _id: number;
private _name: string;
private _effet: string;
private _maListeIngredients: Ingredient[] = [];
public get maListeIngredients(): Ingredient[] {
return this._maListeIngredients;
}
public set maListeIngredients(value: Ingredient[]) {
if (typeof value === ‘undefined’ || value == null) {
console.log(‘est undefined !!’);
value = [];
}
console.log(‘maListeIngredients’);
this._maListeIngredients = value;
}
public get id(): number {
return this._id;
}
public set id(value: number) {
this._id = value;
}
public get name(): string {
return this._name;
}
public set name(v: string) {
this._name = v;
}
public get effet(): string {
return this._effet;
}
public set effet(v: string) {
this._effet = v;
}
public toUpper(): string {
console.log(‘toUpper’);
return this._name.toUpperCase();
}
}
Vous noterez que la classe a bien des propriétés publiques (des getteurs, et des setteurs).
Cachez moi cette classe que je ne saurai voir
Ici, tout est ok. Tout semble parfait … attendez la suite.
- Vous appelez votre httpClient : this._httpClient.get<Potion[]>(url).
- Votre api renvoie du json contenant des fois un tableau d’ingrédients, des fois pas d’ingrédients (tableau vide), des fois même pas la propriété maListeIngredients.
- A l’inscription de votre Observable, vous remarquez que les setters de votre classe ne sont … what ? oui … oui … ils ne sont pas appelés !
- Vous mettez des console.log à tout va dans vos propriétés, rien, que neni !
En fait, le deserializer de HttpClient n’utilise tout bonnement pas votre classe pour instancier les éléments venant du json retourné !
Oui, vous m’avez bien lu !
Quand je préfère créé une classe de ton type plutôt que d’instancier ta classe
Dans Angular, nous avons donc cette classe HttpClient.
Elle va parser le json.
Elle va utiliser le binding par prototypage, et non par instanciation d’une classe !
En somme, elle va :
- Créer un objet vide, donc une instance vide
- Lui appliquer le prototype qu’elle aura lu en lisant la classe qu’on lui aura passée !
Comment s’en sortir ?
Vous devez suivre ces deux étapes :
1. Proposer un constructeur qui utiliser l’objet créé puis prototypé
L’idée ici étant d’assigner l’instance de ce faux objet à notre instance en cours.
export class Potion {
private _id: number;
private _name: string;
private _effet: string;
private _maListeIngredients: Ingredient[] = [];
constructor(obj: any) {
Object.assign(this, obj);
}
Vous noterez l’utilisation du Object.assign.
2. S’attacher à la récupération des données et les transformer avant de les renvoyer à l’Observable
On utilise ici le pipe, et le map.
Nous allons ici, recréer nos vrais objets, avec notre vraie classe, et pour chaque item de la liste récupérée, assigner les valeurs à nos propres instances.
Ca nous donne dans la méthode getAll de notre service :
getAll(): Observable <Potion[]> {
const returnList: Potion[] = [];
const url = environment.api_url;
return this._httpClient.get<Potion[]>(url)
.pipe(
map(function(list: Potion[]): Potion[] {
list.forEach(item => returnList.push(new Potion(item)));
return returnList;
})
);
}
And voilà !