Помощники булевой алгебры для композиции RxJS Observables - PullRequest
0 голосов
/ 22 февраля 2019

Как мне избежать написания сумасшедших combineLatest операторов для вычисления простых логических выражений?

например.это простое выражение едва ли вписывается в элемент управления кодом stackoverflow, и если вы случайно измените порядок параметров, вам будет очень трудно отлаживать!

this.showPlayButton = combineLatest(this.playPending, this.isReady, this.showOverlay)
  .pipe(
   map(([playPending, isReady, showOverlay]) => isReady && !playPending && showOverlay),
   distinctUntilChanged();

1 Ответ

0 голосов
/ 22 февраля 2019

Хорошо, поэтому я был довольно удивлен, что не смог найти существующую библиотеку для этого, поэтому я начал собирать вместе несколько наблюдаемых вспомогательных функций создания.

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)) оба продукта a 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, которая эквивалентна iff SQL Server.Выходные данные: A или B (тип ваших параметров).Это похоже на очень простое выражение if.

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

Composition

С RxJS очень легко составлять их вместе и создавать очень удобочитаемые комбинации.Я часто использую их в Angular-компонентах для специфических свойств пользовательского интерфейса, и их гораздо проще отлаживать.Ниже приведены реальные примеры кода, и они должны быть достаточно понятны.

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

Я добавлю в этот список какЯ придумываю новые.Они охватили большинство ситуаций, с которыми я сталкивался до сих пор.Это, безусловно, может быть превращено в библиотеку, но я не могу формализовать это прямо сейчас, чтобы иметь возможность сделать это.Если что-то уже существует, хотелось бы сравнить: -)

...