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à:
- emettere valori nell’Observable tramite il metodo
next()
quando necessario; - 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).