用于构成RxJS Observables的布尔代数助手

问题描述 投票:1回答:1

如何避免编写疯狂的combineLatest语句来计算简单的布尔逻辑表达式?

例如。这个简单的表达式几乎不适合stackoverflow代码控件,如果你不小心重新排序参数,你将很难调试!

this.showPlayButton = combineLatest(this.playPending, this.isReady, this.showOverlay)
  .pipe(
   map(([playPending, isReady, showOverlay]) => isReady && !playPending && showOverlay),
   distinctUntilChanged();
rxjs boolean-operations
1个回答
1
投票

好吧,我很惊讶我找不到一个现有的库,所以我开始收集一些帮助可观察的创建函数。

Observable<boolean> helpers

这些是“最纯粹的”助手,接受并输出Observable<boolean>。我已经为每个添加了distinctUntilChanged(),这可以防止多次不必要的排放。这是一个非常惰性的运算符,没有shareshareReplay(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());

Boolean converters for truthy / falsey

同一类别中的其他一些“助手”,例如ifTruthyifFalsy,旨在与上述助手一起使用(因为它们需要真正的布尔值)。这些目前使用!=而不是!==,所以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声明。这些的输出是AB(你的参数是什么类型)。这就像一个非常简单的if语句。

export const iff = <A, B>(ifObs: Observable<boolean>, trueValue: A, falseValue: B) => ifObs.pipe(map(value => value ? trueValue : falseValue));

Composition

使用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$)

当我提出新的列表时,我将添加到此列表中。这些已涵盖到目前为止我遇到的大多数情况。这当然可以成为一个库,但我现在无法将其正式化,以便能够做到这一点。如果已存在的东西会喜欢比较:-)

© www.soinside.com 2019 - 2024. All rights reserved.