Blue Flower

Chercher

Le Pattern Observer

Un Observable tire son nom d'un   pattern de conception de logiciel appelé pattern Observer; cette fonctionnalité  (Observable) attache un observer (observateur)  à un producteur  et elle  est définie  avec un objet appelé  Subject; l'objet Subject   maintient une liste d'Observers ( observateurs) qu'il  avertit  à chaque changement d'état introduit par le producteur en appelant les observer par une de leurs méthodes.

Dans la librairie RxJSSubject ( est l'équivalent d'un EventEmitter hérite de Observable et est  le seul moyen de " multidiffuser" une valeur ou un événement à plusieurs observateurs . Un object  Subject a les mêmes opérateurs et méthodes qu'un Objet  Observable. Toutefois  un objet  Subject a un état et une liste d'Observers tandis qu'un Observable est simplement une fonction et en plus les Subject permettent la création  de  sources de données tandis que les Observable émettent les données.

La librairie RxJS combine des Subjects,  des Observables, des Opérateurs, et des Observers. Les Observables envoient des notifications, les Opérateurs les transforment, tandis que les Observers les reçoivent. 

 L'objet Observer se charge de recevoir les notifications de l'Observable. C'est un simple objet qui possède trois méthodes : next(), error() et complete(). La méthode next() est appelée à l'arrivée d'un élément, error() est appelée lorsqu'une erreur survient, et complete() est appelée en fin de séquence. Les Observers doivent se lier à une source de données(producteurs) et retourner une méthode permettant de détruire ce lien. Leur mission est de pousser les données via l'appel de la méthode next(). Une fois cette mission terminée, ils appellent la méthode complète(); toutefois les Observers proposent un certain nombre de garanties :  

  1. Ils empêchent l'appel à next() avec complete() ou error()
  2. Ils n'autorisent plus d'appel avec la désinscription
  3. Les appels à complete() ou error()  font appel à la désinscription
  4. Si l'une des trois méthodes lance une exception, le mécansime de désincription d'execute

Observables dans Angular

Si vous commencez à utiliser Angular, vous rencontrerez probablement des éléments observables lors de la configuration de vos requêtes HTTP.

Exemple

// dans le fichier http.client.ts
 import { Observable } from "rxjs/Rx"
import { Injectable } from "@angular/core"
import { Http, Response } from "@angular/http"
@Injectable()
export class HttpClient {
   constructor(
     public http: Http
 ) {}
   public fetchUsers() {
   return this.http.get("/api/users").map((res: Response) => res.json())
 }
}

 On vient de créer  une simple classe  HttpClient  avec une méthode fetchUsers (ici c'est la méthode map(...) qui retourne un Oservable)  qui renvoie un observable. On va afficher les utilisateurs dans une sorte de liste;  Comme cette  méthode retourne un observable, on doit  s' y abonner. Dans Angular, on  souscrire à un observable de deux manières:
 Manière 1:
On utilise le  pipe asynchrone ( | async  )   dans le template pour souscrire un abonnement à l'Observable. L'avantage ici  est qu'Angular traite l' abonnement pendant le cycle de vie du  composant. Angular s' y abonne et se désabonne automatiquement .  Toutefois il ne faut pas oublier d’importer le module  «CommonModule» dans  AppModule, car le canal asynchrone sera exposé à partir de cela.

 // fichier  user.list.ts

import { Component } from "@angular/core"
import { Observable } from "rxjs/Rx"

// client
import { HttpClient } from "../services/client"

// interface
import { IUser } from "../services/interfaces"

@Component({
    selector: "user-list",
    templateUrl:  "./template.html",
})
export class UserList {

    public users$: Observable<IUser[]>

    constructor(
        public client: HttpClient,
    ) {}

    // faire un appel pour recupérer les "users" à l'initialisation du composant
    // la méthode fetchUsers returne un  observable
    // on l'assigne au propiété "users$"  de la classe
    public ngOnInit() {
        this.users$ = this.client.fetchUsers()
    }
}

...............

<!-- dans le template 
nous utilisons le pipe "async" pour subscribe/unsubscribe automatiquement à l'observable
-->
<ul class="user__list" *ngIf="(users$ | async).length"> <li class="user" *ngFor="let user of users$ | async"> {{ user.name }} - {{ user.birth_date }} </li> </ul>

...

Manière 2:
Nous nous abonnons à l'observable nous-mêmes en utilisant la méthode  subscribe (). Cela peut être utile si vous souhaitez d'abord utiliser les données avant de les afficher. L'inconvénient est que vous devez gérer l'abonnement vous-même.

....

// dans le fichier user.list.ts
import { Component } from "@angular/core"

// client
import { HttpClient } from "../services/client"

// interface
import { IUser } from "../services/interfaces"

@Component({
    selector: "user-list",
    templateUrl:  "./template.html",
})
export class UserList {

    public users: IUser[]

    constructor(
        public client: HttpClient,
    ) {}

    // do a call to fetch the users on init of component
    // we manually subscribe to this method and take the users
    // in our callback
    public ngOnInit() {
        this.client.fetchUsers().subscribe((users: IUser[]) => {

            // do stuff with our data here.
            // ....

            // asign data to our class property in the end
            // so it will be available to our template
            this.users = users
        })
    }
}

....

<!-- dans le template -->
 <ul class="user__list" *ngIf="users.length">
    <li class="user" *ngFor="let user of users">
        {{ user.name }} - {{ user.birth_date }}
    </li>
</ul>

....

Comme on peut le  voir, la logique du modèle est assez similaire à la  précédente manière de faire; la logique du composant peut en réalité devenir bien différente et devenir plus complexe si on opte pour la manière 2. En général, on  recommande de choisir la manière 1.

.....