Autour du codeDevelopper toujours mieux
Posté le

Utiliser une API REST avec Angular2 et le module HTTP

Nous allons reprendre l'application angular2 que l'on avait développé dans le précédent tutoriel. Actuellement les membres de l'équipe affichés par l'application sont codés en dur dans la classe StargateCommand. On va modifier notre application pour aller chercher les équipiers sur un serveur via une API ReST.

Dans le répertoire du projet nommé sg1, on va lancer la commande suivante pour installer le module http d'angular 2

npm install --save @angular/http@2.0.0

Puis on va mettre à jour notre app.module pour importer le module http dans l'application et mettre à jour la configuration de systemjs pour que notre navigateur puisse charger le module http d'angular 2.

import { NgModule }        from '@angular/core';
import { BrowserModule }   from '@angular/platform-browser';
import { HttpModule }      from '@angular/http';
import { StargateCommand } from './app.sgc';
import { TeamMember }      from './app.teammember';


@NgModule({
    imports: [ BrowserModule, HttpModule ],
    declarations: [ StargateCommand, TeamMember ],
    bootstrap:    [ StargateCommand ]
})
export class AppModule { }

(function (global) {
  System.config({

    /* Cette map permet d'indiquer quelle fichier il faut chargé
     * en fonction du nom de module importé.
     */
    map: {
      'app': 'app',
      '@angular/core': 'node_modules/@angular/core/bundles/core.umd.js',
      '@angular/common': 'node_modules/@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'node_modules/@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic': 'node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'node_modules/@angular/http/bundles/http.umd.js',
      'rxjs': 'node_modules/rxjs'
    },

    /* Le point d'entrée du module app est main.js et
     * l'extension des fichiers est .js.
     */
    packages: {
      app: {
        main: './main.js',
        defaultExtension: 'js'
      },
      rxjs: {
        defaultExtension: 'js'
      }
    }
  });
})(this);

Mettre en place un service ReST

Pour rapidement mettre en place un service ReST on va utiliser Woof, Woof est un framework qui permet de créer une API REsT sur une base de données. Comme il est en cours de développement, on va récupérer Woof et basculer sur numéro de commit précis pour ne pas avoir trop de surprise.

git clone https://github.com/Maillol/woof.git

cd woof

git reset --hard 99dbdc1960f509397fe8e15a8ba63e0ce53c17ac

Après téléchargement, l'installation du framework ce fait comme ceci:

cd woof

python3 setup.py install

Dans le répertoire contenant le répertoire sg1, on va créer notre application sgc via la commande

woof startproject sgc

On va se retrouver avec un répertoire sgc au côté du répertoire sg1.

Puis créer notre ressource TeamMember pour stoker les données et lier notre ressource à l'URL /api/team-member.

from woof.resource import Resource, IntegerField, StringField

class TeamMember(Resource):
    name = StringField()
    health = IntegerField()

#!/usr/bin/env python3

from woof.url import EntryPoint
from .models import TeamMember

root_url = EntryPoint('/api')
root_url.crud('team-member/[id]' TeamMember)

On demande au framework de créer la base de données de l'application sgc.

woof createdb --py-path sgc --conf sgc/config.json sgc

Woof est livré avec un mini serveur de développement qui peut également servir des contenus statiques. On va donc l'utiliser pour lancer notre service reste mais également servir notre index.html et l'application Angular 2.

woof runserver --static-dir sg1 sgc --port 8000

On va utiliser l'utilitaire curl pour insérer quelques données dans la base

curl -X POST http://127.0.0.1:8000/api/team-member -d '{"name": "Jack O''neal", "health": 10}'

curl -X POST http://127.0.0.1:8000/api/team-member -d '{"name": "Daniel Jackson", "health": 4}'

curl -X POST http://127.0.0.1:8000/api/team-member -d '{"name": "Samantha Carter", "health": 10}'

curl -X POST http://127.0.0.1:8000/api/team-member -d '{"name": "Teal''c", "health": 10}'

Création d'un service pour dialoguer avec le serveur

On va créer une classe TeamMemberService qui va nous permettre de récupérer les membres des équipes au serveur. Cette classe est décorée avec Injectable. Elle possède une méthode getTeamMembers qui utilise http.get pour récupère les données à l'URL api/team-member http.get va renvoyer un objet Observable de la librairie RxJS sur lequel on applique la méthode map en passant une callback qui transformera nos données en JSON. On va également écrire la méthode addTeamMember qui envoie un membre d'équipe au format JSON pour que l'application Woof l'enregistre.

On va ensuite utiliser notre service dans notre classe StargateCommand en ajoutant un constructeur qui va utiliser le service pour mettre à jour la liste. Angular 2 va fournir automatiquement une instance de TeamMemberService lors de l'instanciation de StargateCommand. On doit cependant déclarer TeamMemberService dans le tableau providers du décorateur Component.

import { Injectable }                    from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class TeamMemberService {
    constructor(private http: Http) { }

    private apiUrl = 'api/team-member';

    getTeamMembers() {
        return this.http.get(this.apiUrl)
                        .map(response => response.json());
    }

    addTeamMember(name: string, health: number) {
        let body = JSON.stringify({ name: name,
                                    health: health });
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        return this.http.post(this.apiUrl, body, options)
                        .map(data => data.json());
    }
}

import { Component }         from '@angular/core';
import { TeamMember }        from './app.teammember';
import { TeamMemberService } from './app.teammemberservice';

@Component({
    selector: 'sgc',
    template: `
        <p>{{ message }}</p>
        <ul>
            <li *ngFor="let member of members">
                <team-member [name]="member.name"
                             [health]="member.health"
                             (missionDone)="onMissionDone($event)"></team-member>
            </li>
        </ul>
        <input #nameMember>
        <button (click)="addMember(nameMember.value, 10)">Add </button>
    `,
    providers: [TeamMemberService]
})
export class StargateCommand {

    message:string;
    members: Array<any> = [];
    teamMemberService: TeamMemberService;

    constructor(teamMemberService: TeamMemberService) {
        this.teamMemberService = teamMemberService;
        this.getMembers();
    }

    getMembers() {
        this.teamMemberService.getTeamMembers().subscribe(
            teamMembers => this.members = teamMembers,
            error => console.log(error)
        );
    }

    addMember(name: string, health: number) {
        this.teamMemberService.addTeamMember(name, health)
                              .subscribe(data => this.members.push(data),
                                         error => console.log(error));
    }

    onMissionDone(teamMember) {
        this.message = teamMember.name + " had just returned from a mission";
    }
}

On va rapidement modifier le gabarit de StargateCommand pour créer un petit formulaire pour saisir le nom d'un nouvel équipier. Remarquez l'utilisation du symbole # pour déclarer les variables #healthMember et #nameMember et les lier à une balise input. Ces variables sont ensuite utilisées par la fonction addMember qui sera appelé sur un clic sur le bouton Add Team Member. Lorsque l'on appelle une méthode du teamMemberService, on appelle la méthode subscribe sur l'Observable retourné en lui passant deux fonctions. La première s'exécute en cas de succès et la seconde en cas d'erreur.

Maintenant que nos modifications sont finie on va exécuter le compilateur TypesScript dans un autre terminal.

cd sg1

tsc -w

Si vous n'avez pas stoppé votre serveur entre temps, le résultat est visible à l'addresse http://127.0.0.1:8000/

Voilà, j'espère que ça vous a plu et que vous n'avez pas eu de difficultés. En attendant un prochain tuto, à vos IDE et bon code laughing