'How to handle ngrx let with many subscribes as ng-container inside ng-container?
I start using ngrx let to using plain objects.
The problem is when I use a lot of observables in my component, I end up with the code which is seems bad (ng-container inside ng-container inside ng-container inside ng-container ...):
<ng-container *ngrxLet="obs1$ as obs1">
<ng-container *ngrxLet="obs2$ as obs2">
<ng-container *ngrxLet="obs3$ as obs3">
<ng-container *ngrxLet="obs4$ as obs4">
<app-comp [data]="obs1" ..>..</app-comp>
<app-comp2 [data]="obs1" [data2]="obs3"..>..</app-comp2>
<app-comp3 [data]="obs1" ..>..</app-comp3>
<app-comp4 [data]="obs1" ..>..</app-comp4>
<app-comp5 [data]="obs4" [data11]="obs1" ..>..</app-comp5>
<app-comp6 [data]="obs2" ..>..</app-comp6>
<app-comp7 [data]="obs3" ..>..</app-comp7>
</app-number>
</ng-container>
Is there a better way to handle this syntax?
Solution 1:[1]
You could combine the observables in the controller using RxJS functions like combineLatest
, zip
and forkJoin
. Each has a specific mechanism and you could find the differences between them here.
Illustration using combineLatest
Controller
combined$: Observable<any>;
ngOnInit() {
this.combined$ = combineLatest(
obs1$.pipe(startWith(null)),
obs2$.pipe(startWith(null)),
obs3$.pipe(startWith(null)),
obs4$.pipe(startWith(null))
).map(([obs1, obs2, obs3, obs4]) => ({ // <-- `map` emits a user-friendly object
obs1: obs1,
obs2: obs2,
obs3: obs3,
obs4: obs4
}));
}
Template
<ng-container *ngrxLet="combined$ as data">
<app-comp [data]="data?.obs1" ..>..</app-comp>
<app-comp2 [data]="data?.obs1" [data2]="data?.obs3"..>..</app-comp2>
<app-comp3 [data]="data?.obs1" ..>..</app-comp3>
<app-comp4 [data]="data?.obs1" ..>..</app-comp4>
<app-comp5 [data]="data?.obs4" [data11]="data?.obs1" ..>..</app-comp5>
<app-comp6 [data]="data?.obs2" ..>..</app-comp6>
<app-comp7 [data]="data?.obs3" ..>..</app-comp7>
</ng-container>
However there is a caveat. combineLatest
and zip
won't start emitting unless each of the source observable has emitted at least once. We can enforce it using the startWith
operator like shown here. But then you need to make sure the first value (null
here) doesn't affect the data binding. You could of course replace *ngrxLet
with *ngIf
to avoid emitting the null
, but then you'd lose the benefits of *ngrxLet
.
Solution 2:[2]
ngrxLet does not seem to support that feature, you could use combineLatest as mentioned in previous answer or try some trcik i used with *ngIf
<ng-container *ngIf="{
obs1: obs1$ | async,
obs2: obs2$ | async,
obs3: obs3$ | async,
obs4: obs4$ | async
} as data">
<app-comp [data]="data.obs1"></app-comp>
<app-comp2 [data]="data.obs1" [data2]="data.obs3"></app-comp2>
<app-comp3 [data]="data.obs1"></app-comp3>
<app-comp4 [data]="data.obs1"></app-comp4>
<app-comp5 [data]="data.obs4" [data11]="data.obs1"></app-comp5>
<app-comp6 [data]="data.obs2"></app-comp6>
<app-comp7 [data]="data.obs3"></app-comp7>
</ng-container>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ruth |
Solution 2 |