Thinking Reactive…as a family!
Clean your room, brush your teeth, and make your reactive programming shine
Hello again! It’s great to be back with another blog post! In this article we’ll expand further in Observables from my previous article about callbacks, promises and observables. (Huh-uh? Haven’t read my previous article? You can find it here.)
The observables, I promise to callback later. | by Eduardo Roth | HeroDevs | Aug, 2022 | Medium
Usually, when we start coding (or learning to code) we go with the imperative approach, which is nothing more than writing the code in the style of a set of do-right-now instructions. It feels more natural, as we’re really used to this, for example:
async loadClients() {
try {
await this.loadingService.showLoader();
await this.clients = this.clientsService.getAll()
await this.toastService.showSuccess('Finished loading clients!');
}
catch (err) {
await this.toastService.showError('An error occurred when loading clients!');
this.errorsService.log(err);
}
finally {
await this.loadingService.hideLoader();
}
}
ngOnInit() {
this.loadClients();
}
As you can already tell, when the component is initialized, we call the function loadClients() that will load all our clients, but what if we wanted to filter by name? We would need to create another function, let’s call it loadClientsByName() or modify the loadClients(). This function accepts a parameter for the name being searched and call it when the user types in an input field or clicks on a “Filter” button.
This seems nice, right? We write our code exactly as we need it… but as soon as we start adding more functionality things can go messy. What if a user adds a new client, then we need to call the loadClients function again, after we confirm the user was added, while checking if the user has entered a name to filter so we can use that value… forget about the filter and oh noes! You have introduced a new bug 😖
Reactivity to the rescue!
Here’s where reactivity (or declarative) programming shines best! By using the power of Observables we can define a set of instructions that the observable is going to do any time a new value is generated (and we can mix multiple observables so we can access different values at the same time, but more of this in following posts).
clients$: Observable<Client[]> = this.clientsService.getAll();
*It’s a common/best practice to add the $ suffix to variables that we know are observables
<div>
<ul *ngIf="clients$ | async as clients; else loading">
<li *ngFor="let client of clients">
{{ client.name }}
</li>
<span *ngIf="clients.length === 0">
No clients
</span>
</ul>
<ng-template #loading>
Loading clients...
</ng-template>
</div>
What if we wanted to add the filter functionality? Well, it’s just a matter of using another Observable (actually a BehaviourSubject which is a Subject which is an Observable that emits events with an initial value) and then getting the results
<div>
<input (change)="filter$.next($event.target.value)" debounce="300" />
<ul *ngIf="clients$ | async as clients; else loading">
<li *ngFor="let client of clients">
{{ client.name }}
</li>
<span *ngIf="clients.length === 0">
No clients
</span>
</ul>
<ng-template #loading>
Loading clients...
</ng-template>
</div>
filter$: BehaviourSubject<String> = new BehaviourSubject('');
clients$: Observable<Client[]> = this.filter$.pipe(
switchMap((value) =>
this.clientsService.getAll({
filters: {
name: value
}
})
),
catchError(err => {
// do something with the error
return [];
});
);
Instead of creating another function to handle the filter, we just modified how we get the clients, and we can continue adding functionality with this, and we would only need to modify the clients$ observable.
Wait… what was that about the family?
Well, I really enjoy sharing real life examples as they are easier for new programmers to grasp, as they provide a better understanding of the concepts behind them.
When you where kids, your parents would give you instructions for doing things like
- Go clean your room
- Go brush your teeth
- Finish your meal
- Do your homework
And the list goes on (if you’re a parent right now you may identify with this). Perhaps you remember that these instructions were given to you exactly at the time your parents needed you to do them. That would make them imperative!
But, what if, instead of that, you were given a set of instructions for your weekdays? Instructions that you would follow no matter what (and if something unexpected happened you could go to your parents in a catchError style). You wake up on Monday and you follow the steps:
- Wake up
- Make your bed
- Take a shower
- Brush your teeth
- Get dressed
- etc…
Your parents need you to do something new? Just add that to your wake up list and you’re good to go, everything will continue working as it should.I know, I know; when you have kids these doesn’t feel like an easy feat to do, but you’ll agree that once your kids start following this list, everything will run smoothly! And the benefit with Observables is that they work from the first time! 😄
Final thoughts
Reactive programming is one of the most complex things to understand in your path with Angular but believe me, once you start with it you won’t want to go back.
In my next post I’ll bring a Rick & Morty example app so we can dive deeper into the differences between imperative and reactive programming.