Combine two or more (boolean) observables on single ngIf using async pipe

后端 未结 3 1783
谎友^
谎友^ 2021-02-13 06:02

Without observables I can write the following line in the HTML template:

3条回答
  •  耶瑟儿~
    2021-02-13 06:10

    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> ) => combineLatest(observables).pipe(map(values => values.every(v => v == true) ), distinctUntilChanged());
    export const allFalse = (...observables: Array> ) => combineLatest(observables).pipe(map(values => values.every(v => v == false) ), distinctUntilChanged());
    export const anyTrue = (...observables: Array> ) => combineLatest(observables).pipe(map(values => values.find(v => v == true) != undefined ), distinctUntilChanged());
    export const anyFalse = (...observables: Array> ) => 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!

提交回复
热议问题