Work with Multiple Observables in Angular’s ngOnInit

Here are a few lessons I learned about working with multiple observables in Angular’s ngOnInit. Recently, I was working on a software project that involved building an Angular component. That component needed to use the results of two subscriptions to determine whether a certain element could be shown.  I ran into an asynchronous race condition issue, as my template was not reloading when the subscription changed.

The Problem

I wanted my HTML template to look as simple as this:


<div *ngIf="shouldShowSecretComponent">
   <topSecret/>
</div>

However, top secret can only occur when two distinct conditions are met.

First Try

At first, I started by subscribing to both calls and then checking both in the template. I initially had the following logic to compute a UI state based on a feature flag and a user permission:


ngOnInit(): void {
  this.featureFlagService.flagChange$.subscribe((flags): void => {
    this.enableFeature = flags[FeatureFlag.flag1] 
  });

this.userPolicyService.getUserPolicy(PolicyNamesType.Policy1).subscribe((res): void => {
    this.hasPermission = res.body.permissions.includes(this.permission);
  });
}

Next, I also tried updating a value in both subscriptions to be used in the template in the if.

That meant adding to lines 5 and 9 above the following


this.shouldShowSecretComponent = this.hasPermission && this.enableFeature` to the end of both subscriptions

Second Try

While the previous method technically works, it has problems:

  • The same computed value (shouldShowSecretComponent) is set in two places.
  • There is no clear control over the order or timing. That’s not a problem in my case, but it’s not ideal.
  • It’s easy to introduce bugs when one observable emits before the other.
  • Testing what would happen if they emitted concurrently wasn’t easy.

Eventually, I came across `combineLatest` and was able to apply it in the following way:


this.shouldShowSecretComponent$ = combineLatest([
   this.featureFlagService.flagChange$,
   this.userPolicyService.getUserPolicy(PolicyNamesType.policy1)
]).pipe(
  map(([flags, res]) => {
    const enablefeature = flags[FeatureFlag.flag1];
    const hasPermission = res.body.permissions.includes(permission1);
    return enablefeature && hasPermission;
  })
);

Result

This final tweak:

  • Allowed me to avoid duplicate assignment.
  • Made it easier to test and reason about what was happening to my component.
Conversation

Join the conversation

Your email address will not be published. Required fields are marked *