I have an ng-container that must show in case some observable has a value, and if not, a ng-template must show:
<ng-container *ngIf="obs$ | async; else template">
<router-outlet></router-outlet>
</ng-container>
<ng-template #template>
<div>
No data
</div>
</ng-template>
However when I log the value of the observable after subscribing to it, I see that it actually has a value, so I don't understand what the issue is here.
Here is how the obs$ observable is set:
obs$: Observable<Promise<SomeClass | null>> =
this.obs2$.pipe(
map(async (foo: SomeOtherClass | null) => {
if (!foo) {
return null;
}
const { id } = foo;
const bar = await this.someService.getBarById(
id
);
return bar;
})
);
You need to use switchMap
instead of going for map
with async and await
, it basically does the same thing.
We initially set the value of null
which is made a observable by using of
, then we use the switchMap
operator, what it does is, it takes the outer observable and waits for the inner one to complete, before completing the sequence.
Until then the condition at *ngIf
will evaluate to false, since the inner value of the observable is checked by the *ngIf
after getting resolved by the async
pipe; since it's falsy, the template will be shown until the value becomes true, which happens after the switchMap completes!
obs$ = of(null).pipe(
switchMap(() => {
// replace below line with: return this.someService.getBarById(id);
return of(true).pipe(delay(5000)); // simulates an API call!
})
);
import { Component } from '@angular/core';
import { provideRouter, RouterOutlet } from '@angular/router';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { CommonModule } from '@angular/common';
import { ChildComponent } from './app/child/child.component';
import { map, timeout, delay } from 'rxjs/operators';
import { of, interval, switchMap } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CommonModule],
template: `
<ng-container *ngIf="obs$ | async; else template">
<router-outlet></router-outlet>
</ng-container>
<ng-template #template>
<div>
No data
</div>
</ng-template>
`,
})
export class App {
name = 'Angular';
obs$ = of(null).pipe(
switchMap(() => {
return of(true).pipe(delay(5000));
})
);
}
bootstrapApplication(App, {
providers: [
provideRouter([
{
path: 'default',
component: ChildComponent,
},
{
path: '**',
redirectTo: 'default',
pathMatch: 'full',
},
]),
],
});