Code, Learn

State management in Angular con BehaviourSubject

28 Luglio 2022 - 3 minuti di lettura

Durante lo sviluppo del nostro progetto con Angular avremo a che fare, presto o tardi, con lo “state management” dei componenti, ovvero dell’insieme delle metodologie utilizzate per gestire i dati interni i quali devono essere condivisibili e fruibili simultaneamente da più componenti e “viste”.

In questo breve articolo vi illustrerò come implementare la gestione dello stato interno attraverso un semplice esempio utilizzando l’oggetto BehaviourSubject della libreria RxJs (ReactiveX).

Buona lettura

Stato interno e BehaviourSubject

Dati quali il nome dell’utente loggato o il contenuto dei campi di  un form di ricerca costituiscono quel che si definisce lo stato interno, un concetto importante perché l’aspetto di un’applicazione Angular, in un qualsiasi istante del suo ciclo di vita, altro non è che lo snapshot del suo stato interno.

Il BehaviourSubject è uno speciale tipo di Observable considerato il tipo di Subject più flessibile e semplice da utilizzare. In Angular, può essere banalmente creato un BehaviorSubject all’interno di un Service per contenere e gestire uno stato. Dato che il Subject è un Observable, si potrà:

  1. emettere valori nell’Observable tramite il metodo next() quando necessario;
  2. sottoscriverlo in diversi componenti e/o servizi allo scopo di rimanere in ascolto di eventuali valori emessi, e reagire di conseguenza.

Vediamo ora un semplice esempio.

La classe CartService

Siamo agli inizi di un progetto, le idee iniziano a prendere forma e vengono incapsulate sotto forma di logica all’interno delle entità fondamentali di Angular. Una possibile rappresentazione dello stadio iniziale potrebbe essere la seguente.

Considerata la bassa complessità e il numero limitato di dipendenze/relazioni potremmo pensare di gestire lo stato interno utilizzando il meccanismo dei servizi, precisamente creando un servizio che ospiterà le informazioni d’interesse e un meccanismo per il “retrieve and share” delle stesse.
Il seguente servizio CartService si occupa della gestione del login e delle informazioni di profilo dell’utente esponendole alle altre entità dell’applicativo tramite BehaviorSubject:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class CartService {
  //  behavior Sub
  public countValue$: BehaviorSubject = new BehaviorSubject(0);
  private items: string[] =[];

  //  Init faze nel costruttore
  constructor() {
    this. countValue$.next(this.items.length);
  }

  getItems(): string[] {
    return this.items;
  }

  getItemCount(): number {
    return this.items.length;
  }

  addItem(item: string) {
    this.items.push(item);

    //  Notify on change
    this. countValue$.next(this.items.length);
  }
}

In questo modo tutti i componenti, le direttive e altri servizi che hanno necessità di utilizzare le informazioni di CartService possono, creando una dipendenza da questo specifico servizio, accedere ai dati attraverso l’Observable countValue$.

Dal punto di vista di un componente quale ad esempio ComponentOne, l’implementazione sarà simile alla seguente.

export class ComponentOne implements OnInit, OnDestroy {
  itemCount: number = 0;
  items: string[] = [];
  private serviceSubsription ;
  constructor(private cartService: CartService) { }

  ngOnInit() {
    this.items = this.cartService.getItems();

    this.serviceSubsription = this.cartService.countValue$.subscribe(
      value =>  { this.itemCount = value; console.log('Value : ' + value); }
    );
  }

  ngOnDestroy() {
    this.serviceSubsription.unsubsribe();
  }
}

Faccio notare che, utilizzando BehaviorSubject, saremo anche in grado di fare un “trace back” agli stati precedenti delle informazioni trattate dal servizio in questione (fondamentale per il time travel dell’applicativo).

Articolo scritto da