使用异步管道在单个ngIf上组合两个或多个(布尔)可观察对象

Seb*_*ian 7 rxjs angular-template angular

如果没有observable,我可以在HTML模板中编写以下行:

<div *ngIf="(myVarA || myVarB) && myVarC !== x"></div>
Run Code Online (Sandbox Code Playgroud)

如果myVar现在所有变量都是可观察的,那么如何翻译这一行呢?

<div *ngIf="((myVarA | async) || (myVarB | async)) && (myVarC | async) !== x">
Run Code Online (Sandbox Code Playgroud)

不起作用.

在另一个问题(在一个Angular*ngIf语句中放置两个异步订阅)可以实现将两个或多个observable组合成一个ngIf的可能性

<div *ngIf="{ a: myVarA | async, b: myVarB | async } as result"></div>
Run Code Online (Sandbox Code Playgroud)

但是,我没有看到在表达式上使用任何布尔运算符(或任何运算符)的可能性,然后用于计算ngIf.

我该如何解决这个问题?请注意,我的所有Observable都使用下面的BehaviorSubject.我认为我想要的是combineLatest在模板中使用运算符.

额外:如果整个表达式的计算结果为true,以便以后在模板中使用,有没有办法提取myVarA的单个值(如myVarA | async as varA)?

Jot*_*edo 12

怎么用combineLatest

例如:

import { combineLatest } from 'rxjs/observable/combineLatest';
import { Observable } from 'rxjs/Observable';    

@Component({...})
export class FooComponent {
  obs1$: Observable<bolean>;
  obs2$: Observable<bolean>;
  obs3$: Observable<bolean>;

  constructor(){
    // set observables
  }

  get combined$(){
    return combineLatest(
      this.obs1$,
      this.obs2$
      this.obs3$,
      (one,two,three)=>(one || two) && three);
  }
}

// template
<div *ngIf="combined$ | async">
Run Code Online (Sandbox Code Playgroud)

请查看以下小提琴以获取指导:

https://jsfiddle.net/uehasmb6/11/

有关更多信息combineLatest运营商在这里

更新:但如果您仍想将所有逻辑保留在模板中,您可以尝试以下方法:

<div *ngIf="((myVarA | async) || (myVarB | async)) && ((myVarC | async) !== x)">
Run Code Online (Sandbox Code Playgroud)

但我建议你不要这样做.保持HTML模板尽可能干净是一种很好的做法.


Sim*_*ver 6

为常见的 AND / OR / ALL 逻辑创建辅助可观察的“生成器”

起初它很容易放置,paymentSelected == false && mode == 'addPayment'但是当您需要添加新条件时,您必须在多个位置更新 UI。

公开一个被调用的可观察对象要好得多showPaymentPanel$,然后在.ts模板文件和模板文件中都清楚它的用途是 : *ngIf="showPaymentPanel$ | async"。这也使测试更容易。

但是我最终得到了很多这样的代码:

showTokenizedPaymentMethods$ = combineLatest(this.hasTokenizedPaymentMethods$, 
                                             this.showAvailablePaymentMethods$).
                              pipe(map(([ hasTokenizedMethods, showAvailableMethods ]) => 
                              showAvailableMethods && hasTokenizedMethods));
Run Code Online (Sandbox Code Playgroud)

那真是一团糟!可能比多个异步管道更糟糕!

所以我创建了辅助函数来生成新的 observables :(在全球某个地方)

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());
Run Code Online (Sandbox Code Playgroud)

注意:这些不是在管道中使用的运算符。

在 ts 文件中,您可以像这样创建 observable(命名为 UI 特定的):

public showPaymentPanel$ = allTrue(this.hasTokenizedPaymentMethods$, this.showAvailableMethods$);
Run Code Online (Sandbox Code Playgroud)

即使现有的 observable 存在,我通常也会创建 UI observables:

public showAccountDetails$ = this.isLoggedIn$;     // this condition is always subject to change
Run Code Online (Sandbox Code Playgroud)

你也可以组合它们:

public showSomethingElse$ = allTrue(this.showPaymentPanel$, this.whateverItIs$);
Run Code Online (Sandbox Code Playgroud)

有时我会将它们暴露给组合在一起的 UI,如下所示:

public ui = { this.showPaymentPanel$, this.showSomethingElse$ );
Run Code Online (Sandbox Code Playgroud)

然后用法:

`*ngIf="ui.showPaymentPanel$ | async"` 
Run Code Online (Sandbox Code Playgroud)

(只ui应该是公开的,所以在模板中它非常清楚你想要允许什么)

尽可能限制在一根管子上!