Practicing my RxJS (no subscription nesting)
Tags: angular, rxjs, typescript
Published: 2021-02-12 08:49:28

I've been listening to the [@AngularShow](https://twitter.com/angularshow) recently, and specifically to their great episodes about RxJS operators. So far two of promised four episodes have been released ([one](https://twitter.com/AngularShow/status/1332400800107175937) and [two](https://twitter.com/AngularShow/status/1341816650752905217)) and they have given me motivation to improve some especially complex bit of piping we have in our application.
The goal of this exercise was to reduce a group of subscriptions I had in my code to a single `subscribe`. It guarantees that all decisions about the code's flow are made in a single place, or at least, as a single flow of decisions.

## Background
*Content Note: I reduced some of the complexity of the real project for the sake of this post, but the general ideas are kept intact.*

The app loads data from two API endpoints. The first endpoint fetches the `structure` of the app and the second endpoint fetches the specific `values` to be placed over the structure. The structure, once loaded, never changes, but the values will keep going back and forth over the API. Because the structure is static, it is kept in the `StructureService` memory while the dynamic values are kept in a store ([ngxs](ngxs.io), in case you're wondering).

Now here's where it starts getting interesting. There are a couple of pages in the module where the user can "land" and we always need to have the values and the structure available as soon as possible. So to keep thing DRY, we call both loading actions directly in the module's constructor.
I'll just say that I was really exited to find that I can make full use of dependency injection in the module's constructor. You can read more in [Ben Nadel's post about this](https://www.bennadel.com/blog/3180-ngmodule-constructors-provide-a-module-level-run-block-in-angular-2-1-1.htm).

## A world of Observables
We have two main observables:

- `structureComplete$` - a `void` observable that completes after structure data is stored in the service.
- `valuesState$` -  the values from the store.

### The `valuesState` from store
```typescript
const valuesState$: Observable<Values> = this.store.select(ValuesState)
```
The store loads and fires its first values immediately with the default of `null`, so we need to skip that and wait for some meaningful content from the endpoint. For that we're going to use the [`skipWhile`](https://rxjs-dev.firebaseapp.com/api/operators/skipWhile) filter. I know that when the data loads successfully, the `values` will never be `null`, so that's what I check for. 

```typescript
const valuesState$: Observable<Values> = this.store 
  .select(ValuesState)
  .pipe(skipWhile((values) => values !== null));
```

### The `structure` complete
The first page that is shown to the user will probably have the `structureComplete$` observable still alive, so we'll [`concat`](https://rxjs-dev.firebaseapp.com/api/index/function/concat) the structure with the values.

```typescript
const valuesPendingStructure$ = concat(
  this.structureService.structureComplete$ as Observable<unknown>, // to prevent the need of mapping
  valuesState$
).pipe(skipWhile<Values>((v) => !v)); // skip the 'structureComplete'
```

I banged my head around this for a while, because I really don't need any data from the `structureComplete$` observable. I just want to know it ... completed, and the information is safely stored in the service. All I really care about, once it's done, are the `values`. I know the structure is in place, but I have to keep watching the values. 

The `Observable<unknown>` calms TypeScript's nerves and lets the `valuesPendingStructure$` variable have the type of `Observable<Values>`. The `skipWhile<Values>((v) => !v)` enables us to skip the first value emitted, which comes from the structure completing.


### Putting it all together

As I mentioned before, There are several pages which look at the structure observable, and while the first page to look at `structureComplete$` will likely find it still loading, later pages will already see it complete and cannot subscribe to it. For that purpose I added a small `isReady` getter to the structure service which defaults to `false`, but sets to `true` at the same time the observable completes and the structure is stored. To decide which of the values observables should I use, I added the [`iif`](https://rxjs-dev.firebaseapp.com/api/index/function/iif) condition observable.

```typescript
// Choose one of two options
// if structure is ready, get down to business
// if it isn't, wait for loadComplete$ and then hand over the values
const values$ = iif(() => this.structureService.isReady, 
  valuesState$, 
  valuesPendingStructure$)
);
```

The `iif` operator can act both as an observable creator and as a pipe operator. In this case, we're using it as an observable creator, based on the value of `isReady`. If the structure has already loaded, subscribe directly to the `values` from the store. If we're still waiting for the structure to load, we'll use the observable that's waiting for it before switching to the values.

That's it. We can now subscribe to a single observable and always get the most up-to-date values

```typescript
$values.pipe(takeUntil(this.destroy$)).subscribe({
  next: values => {
    // ...
  },
  error: error => {
    // Inform the user something went wrong
    // log the error for debugging/improving
  }
});
```
Two important things to remember:
1. We have lots of observables here, and we should always make sure to unsubscribe at some point. I really like the `destroy$` pattern which completes once when the page destroys and gives us a great reference point to unsubscribe.
2. All these observables, something is bound to go wrong. Make sure to always have an error handler on your subscription, so you can inform the user that something went wrong.

## A small final note
As usual, while writing my posts, I had to break multiple time to continue working on this and other tasks. During some of those breaks I kept changing and improving the code, as well as discovering bugs and fixing them. I'm sure there are better ways to do this. For example, I think I can avoid having the `structureComplete` observable and use the original `HttpClient` observable.

If you have any additional thoughts on how this could be made better, I'd appreciate a comment!