Skip to content

Observable dans Angular

Les Observables sont un concept central dans la programmation réactive, notamment utilisés dans des bibliothèques comme RxJS. Au cœur de leur fonctionnement, les Observables sont simplement des mécanismes permettant d'émettre des valeurs sur une période donnée, que ce soit de manière synchrone ou asynchrone.

Imaginez que vous ayez un ami, l'Observable, qui aime vous raconter des histoires. Chaque fois qu'il a quelque chose à dire (une nouvelle partie de l'histoire), il vous le dit. Et quand il a fini, il vous signale qu'il n'a plus rien à dire.

Manipuler un Observable

1. Création d'un Observable

ts
import { Observable } from 'rxjs';

const monObservable = new Observable(observer => {
  observer.next('Salut !'); // Il raconte une partie de l'histoire
  observer.next('Comment ça va ?'); // Il continue
  observer.complete(); // Il signale qu'il a fini
});
import { Observable } from 'rxjs';

const monObservable = new Observable(observer => {
  observer.next('Salut !'); // Il raconte une partie de l'histoire
  observer.next('Comment ça va ?'); // Il continue
  observer.complete(); // Il signale qu'il a fini
});

2. Observer : Qui écoute l'Observable ?

L'observer est la personne (ou la fonction) qui écoute l'Observable. L'observer a trois méthodes :

  1. next : appelée chaque fois que l'Observable émet une valeur.
  2. error : appelée si l'Observable rencontre une erreur.
  3. complete : appelée quand l'Observable a fini d'émettre des valeurs.

3. S'abonner à un Observable

Pour écouter un Observable, vous devez vous y "abonner". Regardons comment faire cela :

ts
const monAbonnement = monObservable.subscribe({
  next: message => console.log(message),
  error: err => console.error('Quelque chose s\'est mal passé :', err),
  complete: () => console.log('L\'histoire est terminée !')
});
const monAbonnement = monObservable.subscribe({
  next: message => console.log(message),
  error: err => console.error('Quelque chose s\'est mal passé :', err),
  complete: () => console.log('L\'histoire est terminée !')
});

4. Synchrone vs Asynchrone

Par défaut, un Observable est synchrone. Cela signifie que lorsqu'on s'y abonne, il exécute son code immédiatement. C'est un appel unicast, c'est-à-dire que chaque abonnement reçoit sa propre exécution indépendante de l'Observable.

Cependant, les Observables peuvent aussi être asynchrones. Supposons que notre ami, l'Observable, veuille nous raconter une histoire en deux parties, avec une pause entre les deux:

ts
const monObservableAsync = new Observable(observer => {
  observer.next('Salut !');
  
  setTimeout(() => {
    observer.next('Je suis revenu après une pause !');
    observer.complete();
  }, 2000);
});

monObservableAsync.subscribe(message => console.log(message));
const monObservableAsync = new Observable(observer => {
  observer.next('Salut !');
  
  setTimeout(() => {
    observer.next('Je suis revenu après une pause !');
    observer.complete();
  }, 2000);
});

monObservableAsync.subscribe(message => console.log(message));

En résumé

Les Observables sont comme des conteurs d'histoires, ils émettent des informations (valeurs) sur une période donnée. Vous pouvez écouter ces histoires en vous y abonnant avec un observer. Même si la base est synchrone et unicast, les Observables peuvent être utilisés de manière puissante pour gérer des opérations asynchrones comme les appels réseau, les événements d'interface utilisateur et plus encore.

Dans Angular

La responsabilité principale des Observables va généralement se trouver dans un service. Les composants vont ensuite s'y abonner pour recevoir les données.

ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'

@Injectable({
  providedIn: 'root'
})
export class MessagesService {

  constructor() {}

  obtenirMessages(): Observable<string> {
    return new Observable(observer => {
      observer.next('Bonjour !');
      setTimeout(() => observer.next('Comment ça va ?'), 1000);
      setTimeout(() => observer.next('Juste un autre message...'), 2000);
      setTimeout(() => {
        observer.next('Fin des messages.');
        observer.complete();
      }, 3000);
    });
  }
}
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'

@Injectable({
  providedIn: 'root'
})
export class MessagesService {

  constructor() {}

  obtenirMessages(): Observable<string> {
    return new Observable(observer => {
      observer.next('Bonjour !');
      setTimeout(() => observer.next('Comment ça va ?'), 1000);
      setTimeout(() => observer.next('Juste un autre message...'), 2000);
      setTimeout(() => {
        observer.next('Fin des messages.');
        observer.complete();
      }, 3000);
    });
  }
}

Le composant souscrit à l'Observable et reçoit les données :

ts
import { Component, OnInit } from '@angular/core';
import { MessagesService } from '../messages.service';

@Component({
  selector: 'app-affichage-messages',
  template: `
    <h2>Messages</h2>
    <ul>
      <li *ngFor="let message of messages">{{ message }}</li>
    </ul>
  `
})
export class AffichageMessagesComponent implements OnInit {

  messages: string[] = [];

  constructor(private messagesService: MessagesService) {}

  ngOnInit(): void {
    this.messagesService.obtenirMessages().subscribe(
      message => {
        this.messages.push(message);
      },
      err => {
        console.error('Erreur lors de la réception du message:', err);
      }
    );
  }
}
import { Component, OnInit } from '@angular/core';
import { MessagesService } from '../messages.service';

@Component({
  selector: 'app-affichage-messages',
  template: `
    <h2>Messages</h2>
    <ul>
      <li *ngFor="let message of messages">{{ message }}</li>
    </ul>
  `
})
export class AffichageMessagesComponent implements OnInit {

  messages: string[] = [];

  constructor(private messagesService: MessagesService) {}

  ngOnInit(): void {
    this.messagesService.obtenirMessages().subscribe(
      message => {
        this.messages.push(message);
      },
      err => {
        console.error('Erreur lors de la réception du message:', err);
      }
    );
  }
}

Aller plus loin: Utiliser l'observable avec le pipe async