Create helper observable 'generators' for common AND / OR / ALL logic
At first it's simple enough to put paymentSelected == false && mode == 'addPayment'
but then when you need to add a new condition you have to update the UI in several places.
It's far better to expose an observable that's called showPaymentPanel$
and then it's clear in both the .ts
and template file exactly what it's for : *ngIf="showPaymentPanel$ | async"
. This also makes it easier to test.
However I was ending up with a lot of code like this:
showTokenizedPaymentMethods$ = combineLatest(this.hasTokenizedPaymentMethods$,
this.showAvailablePaymentMethods$).
pipe(map(([ hasTokenizedMethods, showAvailableMethods ]) =>
showAvailableMethods && hasTokenizedMethods));
And that's a mess! Potentially even worse than multiple async pipes!
So I created helper functions to generate new observables: (globally somewhere)
export const allTrue = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.every(v => v == true) ), distinctUntilChanged());
export const allFalse = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.every(v => v == false) ), distinctUntilChanged());
export const anyTrue = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.find(v => v == true) != undefined ), distinctUntilChanged());
export const anyFalse = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.find(v => v == false) != undefined), distinctUntilChanged());
Note: These are not operators to be use in a pipe.
In the ts file you create the observable (named to be UI specific) like this:
public showPaymentPanel$ = allTrue(this.hasTokenizedPaymentMethods$, this.showAvailableMethods$);
I will typically create UI observables them even if an existing observable exists:
public showAccountDetails$ = this.isLoggedIn$; // this condition is always subject to change
You can also compose them:
public showSomethingElse$ = allTrue(this.showPaymentPanel$, this.whateverItIs$);
Sometimes I'll expose them to the UI grouped together like this:
public ui = { this.showPaymentPanel$, this.showSomethingElse$ );
Then usage:
`*ngIf="ui.showPaymentPanel$ | async"`
(only ui
should be public so in the template it makes it super clear what you want to allow)
Limit to one pipe as much as possible!