Работая с Angular волей-неволей будешь использовать RxJS, ведь он лежит в основе фреймворка. Это очень мощный инструмент для обработки событий и не только. Однако далеко не каждый проект использует его по полной. Часто это просто запросы на бэк, нехитрые преобразования данных и подписка. Мы с Ромой очень любим RxJS и решили собрать несколько интересных кейсов из нашей практики. Мы сделали из этого что-то вроде челленджа на 20 задачек, которые мы предлагаем решить с помощью RxJS и попрактиковать свои навыки. Каждая задачка будет иметь некий бойлерплейт, чтобы вам было просто начать. Под спойлером я положу ссылку на свое решение и небольшое пояснение к нему. В целом задачи будут идти от простого к сложному, а полное собрание с ответами и пояснениями на английском доступно на GitHub. В этой задаче вам предлагается область страницы со всевозможными фокусируемыми элементами внутри. Нужно сделать стрим, который будет отслеживать фокус внутри этой области. StackBlitz Для отслеживания изменения фокуса нам понадобятся события focusin и focusout, поскольку focus/blur не всплывают. Мы будем получать элемент через target и relatedTarget, потому что в момент этих событий мы еще не можем проверить document.activeElement — в коллбэке событий там будет body. Если текущий элемент вне отслеживаемой зоны, будем выдавать null. Так же подобную логику можно вынести в сервис — его потом будет удобно использовать в директивах. Поскольку мы не знаем момент, когда пользователь подпишется на наш стрим, добавим такую конструкцию для получения начального значения при подписке: defer(() => of(documentRef.activeElement)). Остается только собрать все потоки в merge: StackBlitz с готовым решением Хороший пример работы с событиями в RxJS — использование Page Visibility API. Подобный стрим также удобно завернуть в InjectionToken. Бойлерплейта для этой задачи нет ​ Тут ситуация довольно тривиальная. Единственный подвох — начальное значение. Если мы сразу проверим, видима страница или нет, это значение может оказаться неактуальным на момент подписки. Можно использовать defer, как в прошлый раз, а можно начать с произвольного значения, а реальное получать в map ниже:   В качестве бонуса в примере мы превратим этот поток в DI-токен для удобного использования. Этот токен есть в нашей микробиблиотеке токенов на глобальные сущности. StackBlitz с готовым решением Допустим, у нас есть кнопка логина. При нажатии на нее идет запрос на сервер, на это время кнопка будет заблокирована. При успешном логине мы выведем имя пользователя, в противном случае покажем ошибку на 5 секунд и разблокируем кнопку для повторной попытки. В примере ниже заготовлен муляж сервиса логина: StackBlitz Давайте посмотрим, как мы можем ветвить потоки, на примере данной задачи. В такой ситуации типовое начало будет Subject, который мы будем дергать по нажатию на кнопку и Observable, который перебрасывает эти нажатия на запрос на сервер:   Теперь давайте разведем запросы на нужные нам потоки. Обратите внимание на share в конце, который поможет нам избежать повторных запросов на сервер при подписках на эти витки. При ошибке сервис бросит реальную ошибку в поток. Таким образом, имя пользователя будет просто повторной попыткой запроса: Сообщение об ошибке же будет как раз той ошибкой, которую бросит запрос, показанной в течение 5 секунд:   Мы игнорируем элементы и показывает только ошибки. На этом примере хорошо видна разница между repeat и retry: первый перезапускает поток, который успешно завершился, второй перезапускает поток, закончившийся ошибкой. Аналогичным образом мы можем отвести поток, отвечающий за блокировку кнопки. StackBlitz с готовым решением Если ваш сервис репортит прогресс загрузки, удобно было бы отобразить это для пользователя. Например, тут я прикручивал RxJS и прогресс к нативному fetch: Попробуем применить похожую технику ветвления стрима для отображения прогресса. StackBlitz Начнем мы, как и в прошлый раз, с Subject и общего потока. Общий поток мы разведем на два — прогресс и результат. Для первого мы используем фильтрацию:  А для второго — преобразование, чтобы можно было перезапускать процесс:   StackBlitz с готовым решением   Представьте, что вам надо сделать таймер, ведущий отсчет перед повторной отправкой кода. Отличная микрозадача на RxJS. В качестве бонуса в ответе я приведу еще и решение на CSS, которое позволяет форме подтверждения платежа по SMS в Тинькофф работать даже с отключенным JavaScript ​ StackBlitz Для обратного отсчета можно сделать простую утилитную функцию, где мы воспользуемся малоизвестным вторым аргументом takeWhile:   Благодаря второму аргументу, 0, который нарушит условие, тоже провалится дальше. Сам стрим будет просто использовать switchMapTo от Subject, который мы запускаем по кнопке, как в прошлых примерах. Использовать можно так: Заключительный 0 не пройдет ngIf и переключит шаблон назад на кнопку. CSS-решение будет полагаться на анимацию псевдоэлементов и перезапуск ее с помощью :active состояния нажатой кнопки. Загляните в CSS-файл странички с решением: StackBlitz с готовым решением Это была первая неделя нашего челленджа. Впереди вас ждут еще 15 более сложных задач с использованием RxJS. Ссылки будут добавлены ниже по мере публикации. Google Developer Expert | Angular