Comunicazione tra componenti e gestione dello stato
In un precedente articolo vi ho parlato di architettura publisher/subscriber per quello che riguarda webcomponent, e l’approccio utilizzato in Maestrale per la gestione della comunicazione tra diversi webcomponent nei nostri applicativi per la gestione dell’industria 4.0.
Utilizziamo inoltre Angular e React per progetti di maggior complessità e, per quanto architettura publisher/subscriber e comunicazione tramite EventBus sia una strada sempre percorribile per far comunicare componenti, adeguandosi agli standard di framework o librerie abbiamo dovuto rianalizzare il problema e utilizzare diversi approcci in base ai diversi caso d’uso dei progetti.
Uno dei più tipici, riscontrati quasi quotidianamente, è quando i componenti devono ‘dialogare’ non tenendo conto necessariamente del legame di parentela gerarchicamente stretto (parent->child o child->parent) ma mantenendo un generico elemento comune che funge da collante. Si parla in questo caso di ‘sibling-relationship’.
Angular e Directive
In Angular modelliamo questo approccio utilizzando come componente comune una Directive che funge da ‘collante’ per tutti i componenti che, tramite dependency injection, ottengono l’uso della Directive stessa.
In analogia con l’approccio dei webcomponent ci viene in aiuto RxJS e il BehaviorSubject.
Utilizziamo quest’ultimo per mantenere lo stato applicativo (con visibilità private dentro la Directive): l’aggiornamento dello stato avviene trattando il BehaviourSubject [ref] come Observable ed espondendolo ai componenti che necessitano di ottenere/aggiornare lo stato.
Notate bene che tali componenti hanno accesso unicamente al’Observable del BehaviorSubject (stream), non al BS stesso!
In definitiva la nostra Directive si occuperà di:
- Mantenere lo stato
- Gestire l’update dello stato tramite Observable (stream di eventi [ref])
Un esempio pratico!
Cosa se non i colori ci descrivono meglio visivamente i cambiamenti di stato? Probabilmente nulla. Proprio per questo immaginiamoci 2 componenti che interagiscono cambiando una serie di valori HEX che utilizziamo per visualizzare i colori di background e foreground degli stessi.
Lo stato quindi potrebbe essere rappresentato da un oggetto contenente tali valori.
Nella nostra directive instanziamo e inizializziamo nel costruttore lo ‘state’ tramite BehaviourSubject come Observable. Notate che utilizziamo un metodo pubblico ‘updateColor’ che ha accesso all’observable, quindi allo stream, per aggiornare lo stato.
private colorBS = new BehaviorSubject({});
public colorObservable$ = this.colorBS.asObservable();
constructor() {
//default
const def = new ColorObject();
def.bkg = "#D1DB44";
def.frg = "#8B0000";
...
...
this.updateColor(def);
}
updateColor(color: ColorObject) {
this.colorBS.next(color);
}
Sono presenti poi 2 componenti.
color-bkg
Tramite l’observable, il componente si occupa di mostrare i colori sia in HEX sia in RGB: in color$ ho l’observable del BehaviorSubject preso dalla direttiva. Uso l’observable come stream async per accedere allo ‘state’ e quindi mostrarlo
...
constructor(public directive: IntermediaryDirective) {}
ngOnInit() {
this.color$ = this.directive.colorObservable$;
}
<div *ngIf="color$ | async as color" [ngStyle]="{'background': color.bkg}">
<span [ngStyle]="{'color': color.frg}">Changed color text?</span>
</div>
color-picker
Il componente utilizza l’observable per recuperare lo ‘state’ e quindi mostrare i colori del picker. Oltre a questo chiama il metodo ‘updateColor’ della directive per aggiornare lo stato, e quindi notificare gli altri componenti del cambiamento (provate a cambiare colore tramite picker!)
...
constructor(public directive: IntermediaryDirective) {}
ngOnInit() {
this.color$ = this.directive.colorObservable$;
...
}
updateBackground(val) {
this.objColor.bkg = val;
...
this.directive.updateColor(this.objColor);
}
<ng-container *ngIf="color$ | async as color">
<input
#pickerbkg
type="color"
[value]="color.bkg"
(change)="updateBackground(pickerbkg.value)"
/>
...
</ng-container>
Per meglio verificare il codice e l’esempio ecco il playground!
Provate a cambiare colore tramite i picker: noterete che entrambi i componenti vengono notificati del cambiamento di stato da parte del Directive che usano come ‘intermediario’.
Molti altri sono i metodi per gestire lo state e la comunicazione tra diversi componenti in Angular ma il BehaviorSubject è un oggetto abbastanza comune da utilizzare in applicazioni con stili di programmazione reattivi in cui vogliamo che alcuni stati / informazioni centrali siano condivisi.