如何避免编写疯狂的combineLatest
语句来计算简单的布尔逻辑表达式?
例如。这个简单的表达式几乎不适合stackoverflow代码控件,如果你不小心重新排序参数,你将很难调试!
this.showPlayButton = combineLatest(this.playPending, this.isReady, this.showOverlay)
.pipe(
map(([playPending, isReady, showOverlay]) => isReady && !playPending && showOverlay),
distinctUntilChanged();
好吧,我很惊讶我找不到一个现有的库,所以我开始收集一些帮助可观察的创建函数。
Observable<boolean>
helpers这些是“最纯粹的”助手,接受并输出Observable<boolean>
。我已经为每个添加了distinctUntilChanged()
,这可以防止多次不必要的排放。这是一个非常惰性的运算符,没有share
或shareReplay(1)
的更复杂的后果 - 但重要的是要意识到它已被应用。
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());
export const not = (observable: Observable<boolean> ) => observable.pipe(map(value => !value), distinctUntilChanged());
同一类别中的其他一些“助手”,例如ifTruthy
或ifFalsy
,旨在与上述助手一起使用(因为它们需要真正的布尔值)。这些目前使用!=
而不是!==
,所以isDefined(of(null))
和isDefined(of(undefined))
都产生了true
Observable。 isEqual
也不会是一个深刻的比较。
export const isTruthy = <T>(observable: Observable<T>) => observable.pipe(map(obsValue => !!obsValue), distinctUntilChanged());
export const isFalsey = <T>(observable: Observable<T>) => observable.pipe(map(obsValue => !obsValue), distinctUntilChanged());
export const isDefined = <T>(observable: Observable<T>) => observable.pipe(map(obsValue => obsValue != undefined), distinctUntilChanged());
export const isEqual = <T>(observable: Observable<T>, value: T) => observable.pipe(map(obsValue => obsValue == value), distinctUntilChanged());
export const notEqual = <T>(observable: Observable<T>, value: T) => observable.pipe(map(obsValue => obsValue != value), distinctUntilChanged());
还有第三类,如iff
,相当于SQL Server的 iff
声明。这些的输出是A
或B
(你的参数是什么类型)。这就像一个非常简单的if语句。
export const iff = <A, B>(ifObs: Observable<boolean>, trueValue: A, falseValue: B) => ifObs.pipe(map(value => value ? trueValue : falseValue));
使用RxJS,将它们组合在一起并创建非常易读的组合非常容易。我经常在Angular组件中使用这些用于UI特定属性,并且它更容易调试。以下是真实的代码示例,应该是非常自我解释的。
loading$ = not(this.loaded$);
hasSelectedOrder$ = isTruthy(orderId$); // Note: this wouldn't work for something zero
hasSelectedOrder$ = isDefined(orderId$); // uses != undefined
layout$ = iff(deviceType.isDesktop$, 'horizontal', 'vertical');
isMobileOrTablet$: anyTrue(this.isMobile$, this.isTablet);
isTabletOrDesktop$: anyTrue(this.isTablet, this.isDesktop$);
isBusy$ = anyTrue(this.hasBusyTask$, this.busyService.isBusy$);
// very useful for UI (using async pipe)
isAddNewCreditCardSelected$ = isEqual(selectedPaymentMoniker$, 'NEW');
showSavedPayments$ = allTrue(showAvailablePaymentMethods$, hasVault$, not(isAddNewCreditCardSelected$));
showAddCreditCardButton$ = allTrue(showPaymentButtons$, not(showAddCreditCardPanel$), not(showVault$))
showDefaultFooter$ = allTrue(not(this.isWebapp$), this.showDefaultFooter$);
showBusyIndicator$ = allTrue(not(this.pageService.handlesBusyIndicator$), this.busyService.isBusy$) ;
至于最初的问题,它只是变成了这样:
this.showPlayButton$ = allTrue(this.isReady$, not(this.playPending$), this.showOverlay$)
当我提出新的列表时,我将添加到此列表中。这些已涵盖到目前为止我遇到的大多数情况。这当然可以成为一个库,但我现在无法将其正式化,以便能够做到这一点。如果已存在的东西会喜欢比较:-)