DEV Community

Cover image for Handling Falsy Values Emitted from Observables in Angular
ng-conf
ng-conf

Posted on

Handling Falsy Values Emitted from Observables in Angular

Preston Lamb | ng-conf | Sep 2020

In the last year or so, I’ve been fully embracing reactive programming in my Angular apps. That means goodbye subscribe and hello async pipe! It’s been great, but there have been a couple times where the async pipe, in conjunction with the *ngIf structural directive, have not worked as I needed them to. The issue arises when the emitted value from the observable is falsy. When the emitted value is falsy, the *ngIf doesn’t output the content on the screen. I understand why that happens, but many times the falsy value is a valid result and should be displayed. In this post, we’ll cover an easy way to still use the async pipe and *ngIf while still outputting falsy values.

The Issue Explained

Recently, I was working on an application at work where we needed to output the emitted value of an observable. The value was a number with zero being a valid result. We also wanted to display a loading spinner while waiting on data from the API. We did that with the *ngIf structural directive and the else option that goes along with it. Here’s a brief sample of what that looked like:

<div 
  *ngIf=”observableValue$ | async as value; else backupLoader”>
    {{ value }}
</div>
<ng-template #backupLoader>
  <app-loader></app-loader>
</ng-template>
Enter fullscreen mode Exit fullscreen mode

This worked in most situations. Before the observable emitted the response from the API, the loader would show up on the screen. As soon as the value came back, it replaced the loader. The only time it didn’t work was when the emitted value was 0. The loader would stay on the screen and 0 would never show up. The reason for that is that 0 is a falsy value, and thus the *ngIf never stopped showing the loader.

One solution to get around this was to convert the returned value to a string and not a number. I didn’t want to have to alter the value though to get my desired result. I turned to Twitter for a little bit of help and came up with the following solution.

Wrapping Observable Values in Objects

The easiest way to solve this issue is to turn the emitted value from the observable into an attribute on an object. You can do this in the component class file, or directly in the HTML. I’m going to do this directly in the HTML. Here’s what that looks like:

<ng-container 
  *ngIf=”{ observableValue: observableValue$ | async } as data”>
    <div>{{ data.observableValue }}</div>
</ng-container>
Enter fullscreen mode Exit fullscreen mode

What we’ve done here is subscribed to the observable with the async pipe, and put the value into the observableValue attribute of an object. Notice the curly brackets inside the *ngIf structural directive. We then use the as syntax, which renames the variable for use in the template. Inside the double curly brackets, the result is output by accessing the observableValue attribute of the data object.

The *ngIf directive on the ng-container element will now always evaluate to true because we have created that object. So, no matter what value is emitted from the observable, we will output it to the screen.

What About the Loader?

Wrapping the observable in an object was nice, and now we can output falsy values. But if we stop with the example in the previous section we’ll have lost our loader element. So the last part to figure out is how to combine wrapping the observable in an object with showing the loader element before the data is loaded. We can do that by using a second *ngif inside the ng-container, like this:

<ng-container 
  *ngIf=”{ observableValue: observableValue$ | async } as data”>
    <div *ngIf=”
      data.observableValue !== null && 
      data.observableValue !== undefined; 
      else backupLoader”
    >
      {{ data.observableValue }}
    </div>
    <ng-template #backupLoader>
      <app-loader></app-loader>
    </ng-template>
</ng-container>
Enter fullscreen mode Exit fullscreen mode

The ng-container is the same here as it was before. But the div that wraps the data.observableValue output now has a new *ngIf placed on it. The two checks added here are that the observableValue attribute value is not null or undefined. If the value is null or undefined, then the loader is shown. Otherwise, the value is output (including if it’s falsy).

Conclusion

I have been working with Angular for a long time at this point, and am really bummed that I hadn’t figured this out years ago. This is a great way to output falsy values while using the async pipe and the *ngIf structural directive. I will be using this a lot going forward, and hope that this will be useful for you as well.

I also want to give credit to Maxime Robert for writing this great article and to Aaron Frost for the suggestion on Twitter.

ng-conf: The Musical is coming

ng-conf: The Musical is a two-day conference from the ng-conf folks coming on April 22nd & 23rd, 2021. Check it out at ng-conf.org

Thanks to Steven Guitar.

Latest comments (0)