Простое управление компонентами Angular, которые должны сопоставлять множество потоков данных

CombineLatest делает то, что он говорит: он запоминает последнее значение, выданное всеми подписанными потоками, а затем объединяет их. Это чрезвычайно полезно, потому что освобождает вас от проверки всех других значений потока каждый раз, когда один из потоков генерирует.

Если у вас есть компонент Angular с множеством взаимозависимых потоков данных, вы можете использовать шаблон combineLatest из этой статьи, чтобы легко сопоставить данные из этих наблюдаемых.

Зачем нужно одновременно проверять последние значения из разных потоков? Потому что часто бывает, что какой-то сценарий разрешен только в том случае, если данные из разных потоков коррелируют. Например, в приведенном ниже коде частное сообщение из одного потока может отображаться только в том случае, если документ пользователя из другого потока указывает, что пользователь оплатил свою подписку.

Давайте посмотрим на класс компонентов Angular ниже. Я буду показывать весь класс каждый раз, когда буду улучшать код, чтобы вам не пришлось визуализировать, как выглядит новый код.

Наблюдаемые userDoc$ и latestPost$ из хранилища NgRx объединяются в combinedObservable$$, и доступ к любым взаимозависимым данным осуществляется в операторах if перед выполнением каких-либо операций. combinedObservable$$ излучает объединенные потоки данных в виде массива. В этом случае dataArray, который вводится в оператор tap, имеет следующий формат: [{…}, {…}].

Я использовал оператор tap для размещения серии операторов if, которые можно использовать для одновременного доступа к значениям и их действия. Как видите, оператор combineLatest действует как воронка, которая дает вам доступ к последним значениям из любого подписанного потока:

Добавить наблюдаемую переменную с областью действия класса

Следующее часто необходимое требование - отреагировать на изменение значения в переменной с областью действия класса. Изменение может быть вызвано либо выбором пользователя в шаблоне, либо некоторой операцией, которая изменяет значение переменной. Перед выполнением операции может потребоваться сравнить или объединить новое значение переменной с последними значениями из других потоков данных.

Для этого необходимо преобразовать переменную области видимости класса this.classScopedVar в наблюдаемую и передать ее в combinedObservable$$.

Зачем создавать наблюдаемое this.classScopedVar? Почему бы просто не использовать this.classScopedVar в combinedObservable$$? По той простой причине, что combinedObservable$$ испускает объединенный поток только в том случае, если излучает один из его наблюдаемых. Следовательно, необходимо превратить this.classScopedVar в одну из этих наблюдаемых, чтобы ее значение можно было использовать с последними значениями из других потоков.

Я добавил в класс переменные classScopedVar: string; и
classScopedVar$: BehaviorSubject<string>;. Затем я определил this.classScopedVar$ = new BehaviorSubject(this.classScopedVar); и добавил this.classScopedVar$ в поток combinedObservable$$. В третьем заявлении if вы можете увидеть, как значения двух потоков используются вместе (т.е. postObject[classVar]):

Предостережение: все входные потоки должны передать хотя бы один раз, прежде чем поток combineLatest будет излучен. Убедитесь, что все ваши потоки запускаются хотя бы один раз!

Проблема с комбинированными потоками

Все if операторы, которые разрешены до true, будут запускаться при каждом выпуске combinedObservable$$. Это вызывает проблемы, потому что операторы if с неизмененными значениями будут запускаться без необходимости. Например, при каждом выбросе combinedObservable$$ второй оператор if всегда будет запускаться, потому что значения не меняются, тем самым постоянно вызывая askUserToPay().

Есть способы сделать так, чтобы этого не произошло:

  • Используйте ручную переменную с областью действия класса, чтобы при необходимости переопределить отдельный оператор if. Вы можете включать и выключать эту переменную по мере необходимости. В качестве альтернативы переменной класса вы можете использовать другой наблюдаемый поток, такой как BehaviorSubject(), и передать его в объединенный поток, но имейте в виду, что это вызовет еще одно излучение объединенного потока, которое должно обрабатываться любыми другими операторами if.
  • Используйте разные combineLatest потоки, чтобы объединить наблюдаемые в отдельные и разные варианты использования. Не создавайте один гигантский combineLatest поток, который превратит управление своими if заявлениями в кошмар.
  • Используйте distinctUntilChanged, чтобы отфильтровать выбросы, идентичные последним выбросам.
  • Используйте оператор filter, чтобы исключить значения, которые не имеют отношения к делу.
  • Завершите объединенный поток с помощью takeUntil(), если вы уверены, что закончили с ним, или используйте оператор take(#), если вы знаете, сколько выбросов вам нужно.

Я внес эти изменения в приведенный ниже код:

Заключение

Спасибо за прочтение. Если вам понравилась эта статья, подумайте о подписке здесь на членство Medium, чтобы получить доступ к тысячам других статей.

В будущем я опубликую больше советов по Angular, RxJS, NgRx и общему программированию.

Образовательные ресурсы