Angular IDPStrong Junior
Модулі / Дані, моки й RxJS / Практика модуля
🔥 0

Модуль 4 · урок 13 з 13

Практика модуля

Найбільша практика в плані. Тут застосунок нарешті отримує дані — і саме цей модуль лід дивитиметься найуважніше, бо в ньому видно, чи розумієш ти асинхронність, чи просто навчився складати компоненти.

КРОК 1Контракт

Випиши ендпоїнти у файл docs/api-contract.md у стилі Swagger: метод, шлях, параметри, форма відповіді, коди помилок. Опиши DTO окремо від доменної моделі й напиши мапер між ними — як у модулі 2.

Коміт: docs: describe api contract.

КРОК 2Інтерфейс і токен

Опиши інтерфейс TaskApi з усіма методами й типізованими сигнатурами. Заведи InjectionToken і зареєструй мок-реалізацію в app.config.ts. Жодного any.

КРОК 3Генератор і мок

Згенеруй 800–1000 сутностей. Реалізуй мок: фільтрація, сортування, пагінація — усе на боці сервісу, ніби це робить бекенд. Обовʼязково: затримка 300–800 мс, кожен N-й виклик падає з 500, створення з дублікатом коду повертає 409. Стан живе між викликами.

КРОК 4Стор

Зроби TaskStore за патерном з уроку 9: приватні BehaviorSubject для даних, фільтрів, стану завантаження й помилки; публічні Observable тільки для читання; похідний visible$ через combineLatest. Дії — методи, жодного .next() ззовні.

КРОК 5Реактивний екран

Зв'яжи фільтри й пошук: startWithdebounceTime(300)distinctUntilChanged → перерахунок. Компонент — тонкий: жодного subscribe, усе через async pipe або toSignal. Перевір, що швидкий набір у пошуку не створює зайвих запитів.

КРОК 6Стани й помилки

Три стани на екрані: завантаження, помилка з кнопкою «спробувати ще», порожній результат. catchError усередині switchMap, finalize для індикатора. Повідомлення людською мовою, не текст з бекенду. Перевір, що після штучного збою екран продовжує працювати.

КРОК 7Дія зі змінами

Зроби хоча б одну дію, що змінює дані — наприклад, зміну статусу з рядка списку. Оптимістичне оновлення з відкатом при помилці. Оператор — concatMap або exhaustMap, не switchMap.

КРОК 8Перевірка на витоки

Прогони по застосунку пошуком .subscribe(: кожне входження має бути або в сторі з takeUntilDestroyed, або разовою дією. У компонентах — нуль. Потім Heap snapshot: зайди на екран і назад десять разів, порівняй кількість екземплярів компонента.

Модуль зараховано, якщо
  • Дані мокаються строго в сервісі заописаним контрактом; є затримка й керовані збої.
  • Нуль ручних .subscribe() у компонентах.
  • Стан живить UI через BehaviorSubject і combineLatest.
  • Пошук із debounceTime і switchMap; зайвих запитів немає.
  • Помилка не ламає потік: після збою екран продовжує працювати.
  • Є всі стани: завантаження, помилка, порожньо, дані.
  • Заміна мока на HTTP-реалізацію потребує зміни одного рядка.

Що сказати ліду на демо

«Дані мокаються на рівні сервісу за контрактом, який я виписав окремо — компоненти й стор залежать від інтерфейсу, тож підміна на справжній API це один рядок у конфізі. Стан у сторі: приватні BehaviorSubject, назовні тільки читання, похідний список рахується через combineLatest — окремої копії відфільтрованих даних немає. Пошук: debounceTime плюс distinctUntilChanged плюс switchMap, тож гонок відповідей не буває. catchError стоїть усередині switchMap, щоб помилка не вбивала зовнішній потік — після збою пошук працює далі. У компонентах жодного subscribe: усе через async pipe.»

Скільки це займе насправді

Кроки 1–3 — один-два вечори, вони механічні. Крок 4–5 — найважчі, закладай три-чотири вечори й не засмучуйся, якщо перша версія стору вийде кривою: її нормально переписати вдруге. Кроки 6–8 — ще два. Разом це найдовший модуль плану, і це нормально: він же й найцінніший для співбесід.

Далі — модуль 5. Грид: сортування, пагінація, фільтри й вибір рядків. Після цього модуля дані вже будуть, і таблиця стане просто ще одним споживачем твого стору — саме тому порядок модулів такий.

≈ 300 хв · +25 XP за урок