RxJS in Angular – Single Result Data Streams and Async Pipe

In the first and second part of this small series, we have discussed implementations that have flaws and can introduce side-effects to our data flow. We have learned how combining our streams and modeling our our data flow to yield a single result helped with solving the introduced problems.

In this article:

yb
Yannick Baron is architecture consultant at Thinktecture and focuses on Angular and RxJS.

In this third part of the series we want to talk about another advantage when modeling our streams in this manner, specifically in the context of an Angular application.

Composing Data Streams to Yield a Single Result and Leveraging the Async Pipe

Similar to the previous posts, we want to combine streams to retrieve from multiple entities. This time however, we make sure that the final product of our stream is our view model.

For this example we keep it rather simple and combine the request for a user object with the request for a list of roles in our system, so we can fill the view with a nicer representation than just a role id.

				
					// Stream that emits an user object when the userId route param changes
const user$ = this.route.params.pipe(
  switchMap(({ userId }) => requestUser(userId)),
);

// Stream that emits a list of all role objects once
const roles$ = requestRoles();

// Combine both streams
const userAndRoles$ = combineLatest(user$, roles$);

// Transform combined result into UserView model
this.userView$ = userAndRoles$.pipe(
  // Create a UserView object containing the user's properties and its roles
  map(([user, roles]) => ({
    ...user,
    roles: resolveUserRoles(user, roles)
  }))
);
				
			

In the implementation above, we create two streams. One to request and emit a new user object when the userId route param changes, and one to emit a list of all roles once (technically, we could emit live updates of roles, too). We use the combineLatest operator which combines the latest emit of the given streams into a single emit. In our case this serves two purposes. First, once the user$ emits again, which happens when we change the route accordingly, the same pipeline runs and the view will be updated with the new user and its respective roles. Additionally, we cannot display the view until the roles are received. combineLatest will hold back on its emit, until both streams have produced a value.

Finally, we can now use the userView$ stream in our template and subscribe to it via the async pipe. This way we do not even have to handle subscriptions. Another advantage of using the async pipe is that we can easily switch our change detection strategy to OnPush as the template will only need to be updated when our stream emits.

				
					<dl *ngIf="userView$ | async as userView">
  ...
</dl>
				
			

You can see this live on stackblitz.

https://stackblitz.com/edit/angular-ci9trw?file=src/app/app.component.ts

Conclusion

In this series so far, we briefly discussed how combining our streams to yield a single result helps us eradicate potential side-effects. We now illustrated how following this principle can make working with streams in Angular applications easier, as the async pipe implicitly handles subscriptions for us. Ultimately, this comes with performance improvements, as we can make use of the OnPush change detection strategy.

Free
Newsletter

Current articles, screencasts and interviews by our experts

Don’t miss any content on Angular, .NET Core, Blazor, Azure, and Kubernetes and sign up for our free monthly dev newsletter.

EN Newsletter Anmeldung (#7)
Related Articles
Angular
SL-rund
If you previously wanted to integrate view transitions into your Angular application, this was only possible in a very cumbersome way that needed a lot of detailed knowledge about Angular internals. Now, Angular 17 introduced a feature to integrate the View Transition API with the router. In this two-part series, we will look at how to leverage the feature for route transitions and how we could use it for single-page animations.
15.04.2024
.NET
KP-round
.NET 8 brings Native AOT to ASP.NET Core, but many frameworks and libraries rely on unbound reflection internally and thus cannot support this scenario yet. This is true for ORMs, too: EF Core and Dapper will only bring full support for Native AOT in later releases. In this post, we will implement a database access layer with Sessions using the Humble Object pattern to get a similar developer experience. We will use Npgsql as a plain ADO.NET provider targeting PostgreSQL.
15.11.2023
.NET
KP-round
Originally introduced in .NET 7, Native AOT can be used with ASP.NET Core in the upcoming .NET 8 release. In this post, we look at the benefits and drawbacks from a general perspective and perform measurements to quantify the improvements on different platforms.
02.11.2023