This article also exists in: English

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.

write forMaestrale IT

About the author

For the last 15 years, Stefano Frasca has worked with a variety of web technologies both backend and frontend. He is currently focused on front-end development. On his day to day job, he is working as a FullStack developer & Frontend dev fan at Immobiliare Labs IT. He has worked remotely for years, passionate about photography, food and code 😎
Do you want to know more? Visit my website!