Show loading indicator in Angular while waiting for a RxJS observable
Solution 1
If you just want the content to disappear you can emit for example null
when you start fetching a different user:
this.userDetails = this.userId.asObservable().pipe(
switchMap(id => merge(
of(null),
this.getUserDetails(id)
)),
);
The merge
Observable creation method will reemit emit null
and then wait until getUserDetails
completes. You don't even need ng-busy
for this.
Your updated demo: https://stackblitz.com/edit/angular-ng-busy-vwteyf?file=src/app/app.component.ts
Solution 2
you can change your *ngIf
ngIf="userDetails | async as userDetails else #loading"
then
<div #loading>
loading...
</div>
Solution 3
You can use ng-busy (or basically any other npm component out there) for displaying a loading indicator in each http call (promise or observable in your case) in your app.
EDIT: Regarding the fact that you're using Observable, you can use .toPromise in order to work with ng-Busy. Modified the DEMO to show you how to do that.
Here is a Minimal, Complete, and Verifiable example of ng-busy with your code.
Jeremy
Updated on June 13, 2022Comments
-
Jeremy over 1 year
I have an Angular component that listens for a route parameter for a user id to change and when it does it loads the user details. The user details takes a few seconds to return data from the API.
If i'm viewing details for User A and then click to view details on User B, it continues to show User A until User B details are returned a few seconds after my click. Is there a way I can show a loading indicator or just blank it out while it's retrieving data for User B?
User details component:
ngOnInit(): void { this.userDetails = this.route.paramMap.pipe( switchMap((params: ParamMap) => this.userService.getUserDetails(+params.get('userId'))) ); }
User details template:
<div *ngIf="userDetails | async as userDetails"> <h1>{{userDetails.firstName}} {{userDetails.lastName}}</h1> </div>
I would like the user details div to either be blank or show some sort of loading indicator if that inner switchMap is currently running. I know one option would be to have a loading variable that I set to true before the switchMap and false after the switchMap and use that in the *ngIf of the div, but I'm hoping there was a slicker way to not have to have loading variables for EVERY one of these situations.
I have an example StackBlitz: https://stackblitz.com/edit/angular-ng-busy-yxo1gu
The goal is when I click the User B button, User A information should disappear while User B is loading. I have dozens of this scenario in my app so I'm looking for the cleanest way to do this.
-
Jeremy almost 5 yearsThis works for loading the first user. But when I click on a different user it continues to show the first user's information until the new user has finished loading since the component is reused.
-
Derviş Kayımbaşıoğlu almost 5 yearsif you assign
this.userDetails = null
before you start async operation, it will be undefined that moment, so it falls to else block -
Jeremy almost 5 yearsThat's pretty close, but my userDetails is an observable. I took your stackblitz and modified it closer to what I have with userDetails. I have dozens of this scenario throughout my app so I'm looking for the slickest way to remove User A details after the user clicks on the User B button. stackblitz.com/edit/angular-ng-busy-yxo1gu
-
benshabatnoam almost 5 years@Jeremy did you see the updated DEMO? did it help you?
-
Florian Leitgeb about 4 yearsI get this error in my template: Parser Error: Unexpected token # at column 76
-
Hasintha Abeykoon over 2 years@FlorianLeitgeb this might help beginners, so I decided to add this comment, Angular
ngIf
directive else block only expects the template name, so you cannot place#
here with the template name. it should bengIf="userDetails | async as userDetails else loading"
, Also I think you should use<ng-template #loading>...</ng-template>
to setup your loading block in html.