async pipe not subscribing to Observable Angular

1.5k views Asked by At

In Angular (v12) I've got a following component (using dummy variable names):

export class DataDetailsComponent {
    data$: Observable<MyDataModel>;

    constructor(dataService: DataService, route: ActivatedRoute, private router: Router) {
        this.data$ =
            combineLatest([dataService.getDataObservable(), router.events])
                .pipe(
                    filter(([,event]) => event instanceof ActivationEnd),
                    tap(([data, event]) => console.log((<ActivationEnd>event).snapshot.queryParams['id'])),
                    map(([data, event]) => data.filter(c => c.id === (<ActivationEnd>event).snapshot.queryParams['id'])[0]),
                    tap(dataItem => console.log(dataItem))
                );
        this.data$.subscribe(); // console.logs don't work without this
    }
}

And its template:

<div *ngIf="data$ | async as data; else loading">
    <img [src]="data.url" [alt]="data.name">
</div>
<ng-template #loading>Loading...</ng-template>

Data is not being rendered, and the browser console is empty if I don't actually subscribe to the data$. On the other hand, when I do place this.data$.subscribe();, console gets correct output, but the view is still empty (hangs on Loading...).

Can anyone explain what's going on?

2

There are 2 answers

0
dzenesiz On BEST ANSWER

In the end, it was the router event that was not letting observable complete. Apparently, it works if I move navigation from template to .ts and subscribe to ActivatedRoute.params instead of Router.events.

So, the parent component gets:

navigateToDetails(id: string): void {
    this.router.navigate(['/myRoute', id]);
}

instead of using routerLink in template:

<a [routerLink]="['/myRoute']" [queryParams]="{id: item.id}">

And then, on DataDetailsComponent i can do:

constructor(dataService: DataService, route: ActivatedRoute) {
        this.data$ =
            combineLatest([dataService.getDataObservable(), route.params])
                .pipe(map(([data, params]) => data.filter(c => c.id === params['id'])[0]));
    }

Reasons why are beyond me at this point.

1
N Fard On

can be related to your combineLatest. check to see both observables are emitting data.

CombineLatest definition :

Be aware that combineLatest will not emit an initial value until each observable emits at least one value. This is the same behavior as withLatestFrom and can be a gotcha as there will be no output and no error but one (or more) of your inner observables is likely not functioning as intended, or a subscription is late.

you can use startWith operator to set some initial value for observables.