使用异步管道在单个 ngIf 上组合两个或多个(布尔)可观察量
Combine two or more (boolean) observables on single ngIf using async pipe
如果没有 observable,我可以在 HTML 模板中编写以下行:
<div *ngIf="(myVarA || myVarB) && myVarC !== x"></div>
如果所有 myVar
变量现在实际上都是可观察的,我该如何翻译这一行?
<div *ngIf="((myVarA | async) || (myVarB | async)) && (myVarC | async) !== x">
无效。
在另一个问题()中,可以像
一样实现将两个或多个可观察值组合成一个ngIf的可能性
<div *ngIf="{ a: myVarA | async, b: myVarB | async } as result"></div>
但是,我看不到在用于评估 ngIf 的表达式上使用任何布尔运算符(或任何运算符)的可能性。
我该如何解决这个问题?请注意,我所有的 Observables 在下面都使用了一个 BehaviorSubject。我认为本质上我想要的是在模板中使用 combineLatest
运算符。
奖励:如果整个表达式的计算结果为真以供以后在模板中使用(如 myVarA | async as varA
中),是否有任何方法可以提取 myVarA 的单个值?
使用 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">
检查以下 fiddle 以获得指导:
https://jsfiddle.net/uehasmb6/11/
有关 combineLatest
运算符的更多信息 here
更新:但如果您仍想将所有逻辑保留在模板中,您可以尝试类似的方法:
<div *ngIf="((myVarA | async) || (myVarB | async)) && ((myVarC | async) !== x)">
但我建议您不要这样做。保持 HTML 模板尽可能干净是 good practice.
Jota.Toledo 的回答非常好,但是我想跟进我的 'bonus' 关于如何在 ngIf 之后访问表达式的一部分的问题。
基本上,我所做的就是将 class 中的两个或多个可观察值组合起来,如 Jota.Toledo 所述。然而,一个 observable 不是布尔值,而是包含在 ngIf 之后需要可用的项目。
然后只需执行以下操作就很容易了:
newObs = combineLatest(
this.itemObs,
this.boolObs1
this.boolObs3,
(item, bool1, bool2) => ((item || bool1) && bool2) ? item : null
);
因为我们可以依赖 ngIf 中非空对象的真实性。然后 ngIf 看起来像
<div *ngIf="newObs | async as item"></div>
如果在 ngIf 之后需要多个项目,这当然不起作用。在这种情况下,您必须在嵌套的 div 部分上使用两个 ngIf。
为通用的 AND / OR / ALL 逻辑创建助手可观察对象 'generators'
起初放置 paymentSelected == false && mode == 'addPayment'
很简单,但是当您需要添加新条件时,您必须在几个地方更新 UI。
最好公开一个名为 showPaymentPanel$
的可观察对象,然后在 .ts
和模板文件中都清楚它的用途:*ngIf="showPaymentPanel$ | async"
。这也使得测试更容易。
但是我最终得到了很多这样的代码:
showTokenizedPaymentMethods$ = combineLatest(this.hasTokenizedPaymentMethods$,
this.showAvailablePaymentMethods$).
pipe(map(([ hasTokenizedMethods, showAvailableMethods ]) =>
showAvailableMethods && hasTokenizedMethods));
那真是一团糟!可能比多个异步管道更糟糕!
所以我创建了辅助函数来生成新的可观察对象:(全局某处)
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());
注意:这些不是要在管道中使用的运算符。
在 ts 文件中,您创建可观察对象(命名为 UI 特定),如下所示:
public showPaymentPanel$ = allTrue(this.hasTokenizedPaymentMethods$, this.showAvailableMethods$);
我通常会创建 UI 个可观察对象,即使存在现有可观察对象也是如此:
public showAccountDetails$ = this.isLoggedIn$; // this condition is always subject to change
你也可以组合它们:
public showSomethingElse$ = allTrue(this.showPaymentPanel$, this.whateverItIs$);
有时我会将它们暴露给像这样组合在一起的 UI:
public ui = { this.showPaymentPanel$, this.showSomethingElse$ );
然后用法:
`*ngIf="ui.showPaymentPanel$ | async"`
(只有 ui
应该是 public 所以在模板中它非常清楚你想要允许什么)
尽量限制在一根管道内!
如果没有 observable,我可以在 HTML 模板中编写以下行:
<div *ngIf="(myVarA || myVarB) && myVarC !== x"></div>
如果所有 myVar
变量现在实际上都是可观察的,我该如何翻译这一行?
<div *ngIf="((myVarA | async) || (myVarB | async)) && (myVarC | async) !== x">
无效。
在另一个问题(
<div *ngIf="{ a: myVarA | async, b: myVarB | async } as result"></div>
但是,我看不到在用于评估 ngIf 的表达式上使用任何布尔运算符(或任何运算符)的可能性。
我该如何解决这个问题?请注意,我所有的 Observables 在下面都使用了一个 BehaviorSubject。我认为本质上我想要的是在模板中使用 combineLatest
运算符。
奖励:如果整个表达式的计算结果为真以供以后在模板中使用(如 myVarA | async as varA
中),是否有任何方法可以提取 myVarA 的单个值?
使用 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">
检查以下 fiddle 以获得指导:
https://jsfiddle.net/uehasmb6/11/
有关 combineLatest
运算符的更多信息 here
更新:但如果您仍想将所有逻辑保留在模板中,您可以尝试类似的方法:
<div *ngIf="((myVarA | async) || (myVarB | async)) && ((myVarC | async) !== x)">
但我建议您不要这样做。保持 HTML 模板尽可能干净是 good practice.
Jota.Toledo 的回答非常好,但是我想跟进我的 'bonus' 关于如何在 ngIf 之后访问表达式的一部分的问题。
基本上,我所做的就是将 class 中的两个或多个可观察值组合起来,如 Jota.Toledo 所述。然而,一个 observable 不是布尔值,而是包含在 ngIf 之后需要可用的项目。
然后只需执行以下操作就很容易了:
newObs = combineLatest(
this.itemObs,
this.boolObs1
this.boolObs3,
(item, bool1, bool2) => ((item || bool1) && bool2) ? item : null
);
因为我们可以依赖 ngIf 中非空对象的真实性。然后 ngIf 看起来像
<div *ngIf="newObs | async as item"></div>
如果在 ngIf 之后需要多个项目,这当然不起作用。在这种情况下,您必须在嵌套的 div 部分上使用两个 ngIf。
为通用的 AND / OR / ALL 逻辑创建助手可观察对象 'generators'
起初放置 paymentSelected == false && mode == 'addPayment'
很简单,但是当您需要添加新条件时,您必须在几个地方更新 UI。
最好公开一个名为 showPaymentPanel$
的可观察对象,然后在 .ts
和模板文件中都清楚它的用途:*ngIf="showPaymentPanel$ | async"
。这也使得测试更容易。
但是我最终得到了很多这样的代码:
showTokenizedPaymentMethods$ = combineLatest(this.hasTokenizedPaymentMethods$,
this.showAvailablePaymentMethods$).
pipe(map(([ hasTokenizedMethods, showAvailableMethods ]) =>
showAvailableMethods && hasTokenizedMethods));
那真是一团糟!可能比多个异步管道更糟糕!
所以我创建了辅助函数来生成新的可观察对象:(全局某处)
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());
注意:这些不是要在管道中使用的运算符。
在 ts 文件中,您创建可观察对象(命名为 UI 特定),如下所示:
public showPaymentPanel$ = allTrue(this.hasTokenizedPaymentMethods$, this.showAvailableMethods$);
我通常会创建 UI 个可观察对象,即使存在现有可观察对象也是如此:
public showAccountDetails$ = this.isLoggedIn$; // this condition is always subject to change
你也可以组合它们:
public showSomethingElse$ = allTrue(this.showPaymentPanel$, this.whateverItIs$);
有时我会将它们暴露给像这样组合在一起的 UI:
public ui = { this.showPaymentPanel$, this.showSomethingElse$ );
然后用法:
`*ngIf="ui.showPaymentPanel$ | async"`
(只有 ui
应该是 public 所以在模板中它非常清楚你想要允许什么)
尽量限制在一根管道内!