Vincenzo Saccotelli • 2023-03-17
Scopri l'arte della programmazione reattiva con Angular e RxJS. Gestisci eventi asincroni e ottimizza il tuo codice. Impara come utilizzare Observable, Observer, Subscription e gli operatori di RxJS per creare flussi di dati e manipolarli.
RxJS è una libreria event-driven di Javascript, utilizzabile con React, con la quale è possibile gestire sequenze di eventi che possono avvenire in un’applicazione web.
RxJS, in particolare, è un ottimo strumento che ci permette di gestire gli eventi asincroni in modo molto efficiente. Reactive Extensions Library for JavaScript ci viene in aiuto quando abbiamo la necessità di sviluppare un applicativo con un tipo di programmazione reattiva.
Avere anche una conoscenza di base di questa libreria può aiutare a scrivere del codice più pulito. In molti casi anche ad avere una gestione dello stesso codice più semplice (con le dovute eccezioni ovviamente!).
Indice
La programmazione reattiva è un modo di programmare in cui l’applicazione è sempre pronta a rispondere a qualsiasi cambiamento che avviene all’interno del sistema.
Con la programmazione reattiva si scrive codice che è costantemente in ascolto di eventi o di cambiamenti nello stato dell’applicazione.
In RxJS, questo è possibile attraverso l’uso degli Observable. Vediamo cosa sono e come funzionano.
In RxJs un flusso di dati è chiamato Observable, e per poter accedere a questi dati abbiamo bisogno di effettuare una sottoscrizione (Subscription). Ogni volta che avremo dei nuovi dati sullo stream verrà eseguita una funzione, tutto in maniera asincrona.
Quando utilizziamo dei tipi di dati Observable, abbiamo bisogno di un oggetto che osservi il dato e che sia in grado di riceverne i valori. Stiamo parlando di un Observer.
Pensiamo ad esempio, un form che riceve i dati inseriti dall’utente. Quando l’utente compila il form, i dati vengono inviati come flusso di dati all’Observable, che a sua volta notifica all’Observer i dati inseriti dall’utente. Ad esempio tramite una funzione di backend.
Esempio di observer
Fonte: RxJS Dev
Possiamo notare che un Observer possiede più callback dove:
– next: è la funzione, sempre presente, che viene chiamata ogni volta quando viene inviato un nuovo dato allo stream;
– error: è il metodo che viene utilizzato dall’Observer quando riceve un errore. In questo caso l’esecuzione dell’ Observable viene interrotta;
– complete: questa funzione viene utilizzata per ricevere una notifica senza valore;
Bene, ma adesso dobbiamo capire come possiamo creare i nostri flussi ed eventualmente manipolarli.
Niente paura, RxJs mette a nostra disposizione gli operatori, vediamo come fare.
In RxJS gli operatori di creazione più comuni troviamo of(), from(), fromEvent(source, ‘event’).
Gli operatori of() e from() sono simili perché entrambi servono a creare dati observable e a inviarli uno dopo l’altro. La differenza è che from() usa i dati da un Array o una Promise, mentre of() li crea direttamente.
Invece, con l’operatore fromEvent(source, ‘event’) possiamo creare un observable partendo da un evento. Nella funzione si devono utilizzare come input due argomenti: il primo, la sorgente che emette l’evento e, secondo, il tipo di evento.
Ad esempio, pensiamo a un campo di ricerca dove andiamo ad intercettare l’evento keyup() della tastiera. Con fromEvent() possiamo catturare l’evento, isolare il valore dell’input ed eventualmente sfruttarlo per fare una chiamata HTTP a un server back-end.
Oltre alla creazione di Observable, con RxJs è possibile filtrare, mappare, concatenare, combinare, passare da un flusso ad un altro etc. con semplicità grazie all’operatore pipe.
Ma andiamo con ordine…
Prima di fare la Subscription al nostro Observable è possibile che i nostri dati debbano essere mostrati diversamente da come arrivano dallo stream; quindi, abbiamo bisogno di apportare delle modifiche.
Pensiamo ad esempio ad un flusso di numeri che ci arriva sotto forma di Observable, però a noi interessa avere tutti quei numeri moltiplicati per dieci in output.
Ok, cosa facciamo?
Sicuramente per prima cosa creiamo un flusso e, dato che i nostri numeri non fanno parte di un Array e non derivano da una Promise, utilizziamo l’operatore of(). Prima di sottoscriverci all’observable, concateniamo l’operatore pipe() e al suo interno andiamo ad utilizzare map(), che prenderà ogni singolo dato e noi gli diremo di moltiplicarlo per dieci.
Modifica dati observable
Allo stesso modo possiamo utilizzare altri operatori che RxJs ci mette a disposizione anche in combinazione tra di loro. Ad esempio, supponiamo di voler moltiplicare per dieci solo i multipli di due; in questo caso prima di map() utilizzeremo l’operatore filter().
Filtrare i numeri con gli Operatori RxJS
Per capire il comportamento di un Observable, soprattutto dopo aver applicato uno o più operatori, possiamo avvalerci di uno strumento, i cosiddetti Marble Diagrams.
Marble Diagrams
Prendiamo un esempio che si trova sulla documentazione ufficiale e cerchiamo di interpretarlo.
Flusso di dati observable
Nella prima parte viene rappresentato il flusso di dati iniziale che è composto da numeri, quindi abbiamo un Observable.
Modifica stream in RxJS
In questo rettangolo, viene inserito solitamente l’operatore che stiamo utilizzando per modificare i dati dello stream.
Observable filtrato
Alla fine, viene mostrato l’observable con i dati filtrati.
Dalla versione di Angular 2.0 è stata introdotta la libreria RxJs che fornisce, appunto, il tipo di dato Observable.
Un esempio, probabilmente il più utilizzato, è quello relativo alle chiamate HTTP dove attraverso i moduli HttpClientModule e HttpClient abbiamo la possibilità di interrogare un server e farci restituire i dati sottoforma di Observable, con tutti i benefici che esso comporta.
Supponendo che vogliamo recuperare una lista di utenti da un server tramite una chiamata API, con Angular andremo ad utilizzare i moduli sopra citati che trasformeranno e ritorneranno la chiamata in un Observable; in questo modo:
Recuperare una lista di utenti tramite chiamata API
Il metodo getUsers() è ciò che ci interessa ed è lì che sarà racchiuso il nostro flusso di dati, ma fin quando non effettuiamo una sottoscrizione, non possiamo usufruire di questi dati. Quindi il secondo passaggio sarà quello di fare una Subscription, raccogliere i dati e mostrarli graficamente, andiamo!
Subscription
In un altro componente, nel suo file .ts (TypeScript), andiamo a fare la sottoscrizione all’Observable, e dato che adesso ci troviamo nell’oggetto Observer, tramite il metodo next() possiamo prendere i nostri dati (array di utenti in questo caso) e copiarli in una variabile (users) del nostro componente che andremo ad utilizzare nel file .html dove verranno mostrati i nomi degli utenti.
Metodo next()
Alla fine, possiamo usare il nostro array di utenti e mostrarlo graficamente.
Output array di utenti
Come negli esempi precedenti, possiamo concatenare l’operatore pipe() e apportare delle modifiche al nostro flusso prima di fare la Subscription secondo le nostre esigenze di sviluppo!
Se non avessimo utilizzato il BehaviorSubject nel service, avremmo appesantito l’applicativo sia lato back-end ma anche lato front-end dato che per avere la nostra lista di utenti dobbiamo per forza richiamare il server!
Ovviamente qui parliamo di soli due componenti, ma immaginate in un’applicazione con centinaia di componenti e service quante chiamate al server potremmo risparmiare.
Un altro utilizzo della programmazione reattiva in Angular è relativo ai form, e proprio per questo ci mette a disposizione un modulo, ReactiveFormsModule, con il quale possiamo gestire i nostri form in modo molto efficiente grazie a dei controlli che faremo direttamente nel nostro file .ts.
I Subject sono speciali tipi di Observable che servono per effettuare il multicasting di informazioni, ovvero trasmettere un’informazione a più Observers.
Subject per il multicasting di informazioni
Quello che vedremo in console derivante dal codice scritto sopra sarà:
Visualizzazione in console
Notiamo che il valore 1 non viene mostrato, questo perché i Subject informano i Subscribers dei valori notificati solo dopo una Subscription da parte di un Subscriber, mentre il valore 2 viene ricevuto solo dalla prima Subscription ma non dalla seconda.
La particolarità dei Subject è che, invocando semplicemente il metodo next(), ci consentono di emettere dei valori.
Esistono 4 tipologie di Subject, ma noi faremo un esempio utilizzandone una, ovvero BehaviorSubject, ma ovviamente vi invito ad approfondire le altre.
Un’integrazione in un progetto Angular di BehaviorSubject potrebbe risultarci efficace nel momento in cui abbiamo bisogno di mostrare dei dati a più componenti, memorizzando quest’ultime in un service.
Il nostro service risulterà così:
Operatore BehaviorSubject
Un BehaviorSubject ha bisogno di un valore iniziale, e dato che noi sappiamo che il server ci ritornerà un array di utenti lo inizializziamo con array vuoto.
Con l’operatore pipe() di RxJs, vado a mappare con map() la response (se esiste) del server e passarla con il metodo next() al nostro BehaviorSubject.
Operatore pipe()
Vediamo adesso come recuperare questi dati e con una sola Subscription fatta nel componente AppComponent in questo caso, possiamo poi mantenere in memoria la lista di utenti e mostrarla anche in un altro componente che chiameremo ChilComponent.
Nel file .ts di AppComponent inizializziamo una proprietà users$ di tipo Observable, questo perché andremo ad utilizzare in metodo asObservable() sul nostro BehaviorSubject che restituirà un nuovo Observable che possiamo utilizzare nel file .html con la pipe async di Angular.
Metodo asObservable() sul BehaviorSubject
Quindi avremo:
Risultato dell’asObservable() sul BehaviorSubject
Nel componente ChilComponent avremo questa situazione:
Visualizzazione ChilComponent
Non dovremo più fare una chiamata al server qui, ma possiamo direttamente usufruire dei dati che il BehaviorSubject manterrà in memoria. In questo modo ‘alleggeriamo’ anche il back-end gestendo uno stato lato front-end.
Quindi al click su ‘Mostra Lista Figlia’ avremo la nostra lista di utenti senza richiamare un back-end:
Lista Figlia
RxJs è un ottimo strumento per lo sviluppo di applicazioni reattive ma anche per gestire degli stati.
Il suo utilizzo in Angular è molto presente e gli strumenti che ci mette a disposizione sono davvero tanti e molto efficaci. Con questa libreria possiamo dare una spinta ai nostri progetti, renderli più leggibili a livello di codice e anche, perché no, più moderni!
Vi invito, se questo argomento vi affascina, ad approfondire l’uso dei Subject e ad usare gli operatori RxJs nei vostri progetti sia di studio che reali. Front-End.
Vincenzo Saccotelli
L’autore di questo articolo lavora in Ulixe dal 2022 come Sviluppatore Front-End. Ama le sfide e per lui l’apprendimento non ha mai fine. È una persona un po’ vintage, il che spiega la sua passione per la fotografia analogica, la musica anni 80-90 e le camicie a quadri di flanella. Il viaggio è una costante nella sua.
Torna sul nostro blog per approfondimenti dal mondo dello sviluppo Front-End!
Get in touch
Ulixe Group S.r.l. Copyright © Ulixe Group S.r.l. | Lungo Dora Pietro Colletta, 67, 10153, Torino, Italia | Partita IVA IT03305250122 | Numero Rea TO1173020