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