05. Ajout d'un service métier et alimentation de la page liste

Dans cette leçon, nous allons implémenter un service de gestion des séries qui simulera l’interrogation d’un service REST. Puis, nous l’utiliserons pour créer un tableau listant les séries.

Ajout de données factices

Tout d’abord, nous allons créer un fichier api/shows.js qui contiendra un jeu de données au format JSON. Dans cet exemple, j’ai mis 3 séries mais vous pouvez mettre ce que vous voulez.

{
    "entities": [
        {
            "id": 1,
            "title": "Sense 8",
            "year": 2015,
            "seasons": [
                {
                    "number": 1,
                    "episodes": [
                        {
                            "number": 1,
                            "view": true
                        },
                        {
                            "number": 2
                        },
                        {
                            "number": 3
                        },
                        {
                            "number": 4
                        }
                    ]
                },
                {
                    "number": 2,
                    "episodes": [
                        {
                            "number": 1
                        },
                        {
                            "number": 2,
                            "view": true
                        }
                    ]
                }
            ]
        },
        {
            "id": 2,
            "title": "Ascension",
            "year": 2012
        },
        {
            "id": 3,
            "title": "Utopia",
            "year": 2001
        }
    ]
}

entities n'est pas obligatoire, mais je préfère mettre mes données dans une propriété spécifique en cas de nécessité à passer d’autres informations. On retrouvera donc cette clé lors de la récupération des données JSON.

Création du model associé

Comme nous travaillons en TypeScript, nous allons typer ces données en créant les entités associées. Pour cela, on ajoutons dans le module show un répertoire models qui contiendra les 3 objets métiers :

Episode

export class Episode {

    constructor(number:number) {
        this.number = number;
    }

    number:number;
    view:boolean;
}

Contient une propriété number qui est le numéro de l'épisode et une propriété view qui indique si l'épisode a déjà été visionné.

Season

import { Episode } from './episode';

export class Season {

    constructor(number:number) {
        this.number = number;
    }

    number:number;
    episodes:Episode[];
}

Contient une propriété number qui est le numéro de la saison episodes qui est la liste des épisodes de la série.

Show

import { Season } from './season';

export class Show {
    id:number;
    title:string;
    year:number;
    seasons:Season[];
}

Contient un identifiant, le titre, l'année et une liste de saisons.

Création du service de gestion des séries

Maintenant que nous avons nos objets métiers, nous allons créer un service qui va les manipuler. Etant donné que nous classons nos éléments par typologie, nous allons également créer un sous répertoire au module show qui se nomme services. A l'intérieur, on va placer notre service ShowRepository. Pour créer un service, c’est comme pour créer un composant, il faut créer une class puis la décorer avec cette fois-ci @Injectable().

Pour pouvoir faire une requête, nous allons devoir utiliser le service http fourni par Angular. Quand on veut utiliser un service, il faut d’une part l’importer et ensuite l’injecter dans notre class. Pour injecter un élément, il faut passer par le constructeur et le spécifier en argument en donnant un nom de variable puis le type à injecter.

Pour travailler en asynchrone, Angular 2 utilise les Observable de la librairie RxJs. Les Observable sont des objets sur lesquels il sera possible de s’abonner pour effectuer une action lorsqu’il y a de nouvelles données. Cet abonnement nous le ferons dans le composant appelant le service.

// src/show/services/show-repository.service.ts
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import { Observable, Observer } from 'rxjs/Rx';

import { Show } from '../models/show'

@Injectable()
export class ShowRepository {

    constructor(private _http:Http){

    }

    public getShows() : Observable<Show[]> {

        return this._http.get('api/shows.js')
                    .map((res:Response) => <Show[]>res.json().entities)
                    .catch(this.error);
    }

    private error(error: Response) {
        console.log(error);
        return Observable.throw("Request error");
    }

}

Ici, nous injectons donc dans la propriété _http le service Http par le biais du constructeur. Ensuite, on utilise la méthode get() de ce service pour requêter le jeu de données créé précédemment. Enfin, la méthode map() va permettre de récupérer dans la réponse les données et de les caster dans le type attendu.

Déclaration du service

Avant de pourvoir utiliser notre service, il convient de le déclarer au niveau de note module show. Pour cela, il faut se rendre dans la configuration du module show.module.ts, l’importer et l’enregistrer au niveau des providers.

import { ShowRepository } from './services/show-repository.service';

@NgModule({
    imports:[CommonModule, FormsModule, RouterModule.forChild(showRoutes)],
    declarations:[ShowListComponent, ShowEditComponent, EpisodeDirective, ProgressPipe],
    providers: [ShowRepository]
})
export class ShowModule{}

De plus, comme nous utilisons le service Http qui est dans HttpModule , nous devons également l’ajouter à notre application. Nous allons l’ajouter dans app.module.ts pour le rendre accessible dans toute notre application.

import { HttpModule } from '@angular/http';

@NgModule({
    imports: [BrowserModule, HttpModule, HomeModule, ShowModule, RouterModule.forRoot(appRoutes)],
    declarations: [AppComponent],
    providers:[MessageService],
    bootstrap: [AppComponent]
})
export class AppModule {}

Utilisation du service dans un composant

Nous allons reprendre le ShowListComponent pour y injecter ce nouveau service. Cela se fait de la même manière que Http précédemment. Il faut d’une part, l’importer et d’autre part, le mettre comme paramètre du constructeur. Nous utiliserons ce service pour alimenter une propriété

Nous utiliserons ce service pour alimenter une propriété shows qui deviendra le tableau de Show bindé par la suite dans notre vue.

Comme il faut faire cette alimentation à l’initialisation du composant, nous allons implémenter l’interface OnInit qui définit la méthode ngOnInit().

import { Component, OnInit } from '@angular/core';

import { ShowRepository } from '../services/show-repository.service';

import { Show } from '../models/show';

@Component({
    selector: 'show-list',
    templateUrl: './app/show/components/show-list.component.html'
})
export class ShowListComponent implements OnInit {

    shows: Show[];

    constructor(private _showRepo: ShowRepository){
    }


    ngOnInit(){
        this._showRepo.getShows().subscribe((shows:Show[]) => this.shows=shows);
    }

}

Ne reste plus qu’à souscrire à la méthode getShows() afin d'alimenter la propriété du composant avec les données retournées.

La vue HTML

Ensuite, nous n'avons plus qu’à utiliser la propriété shows dans la vue HTML de notre composant pour y construire le tableau listant les séries grâce aux directives ngIf et ngFor.

<div class="row">
    <div class="column">
        <h1>
            Shows <small>List</small>
        </h1>
    </div>
</div>

<div class="row">
    <div class="column">

        <table>
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Title</th>
                    <th style="width:80px"></th>
                </tr>
            </thead>
            <tbody *ngIf="shows">
                <tr *ngFor="let s of shows">
                    <td>{{s.id}}</td>
                    <td>{{s.title}}</td>
                    <td></td>
                </tr>
            </tbody>
        </table>

    </div>
</div>

Conclusion

Après avoir alimenté ce tableau, nous l'enrichirons dans la prochaine leçon de 2 fonctionnalités : la suppression d’un élément et la redirection vers l’édition d’un élément.