Практическая работа к уроку 7 (занятие 2)

HTTP, mock API и ошибки

Подключите сервис к mock API через HttpClient: CRUD, обработка загрузки, пустого и ошибочного состояний.

В этой работе мы учимся «доставать» данные из интернета (точнее — из локального сервера) и показывать их в приложении. Представьте, что ваше приложение — это покупатель в магазине: оно делает заказ (HTTP-запрос) и ждёт посылку (ответ с данными). Всё остальное — это про то, как не запутаться, если магазин закрыт, пуст или ошибся.

Что вы узнаете

💡 Четыре главные аналогии этого урока.
  • HTTP-запрос — заказ в магазине (ты подходишь к прилавку и просишь товар).
  • HttpClient — курьер, который везёт заказ.
  • catchError — сетка безопасности (ловит падение, чтобы не разбить программу): если запрос упадёт, он не разобьёт программу.
  • loading / empty / error — три состояния посылки: «в пути», «доставлена», «потеряна».

Задание 1. Включение HttpClient

Контекст. Прежде чем делать запросы, Angular надо сказать: «У нас есть курьер, разреши им пользоваться». Делается это один раз при запуске приложения (в файле, где вызывается bootstrapApplication).

Аналогия из жизни 💡 Это как выдать сотруднику пропуск на курьерскую службу. Без пропуска он не сможет забирать посылки, как бы вы его ни просили.

Пошаговый разбор.

Что увидит студент. Сам по себе этот код ничего на экране не рисует. Но если его забыть — все запросы дальше будут падать с ошибкой «No provider for HttpClient».

import { provideHttpClient } from '@angular/common/http';

bootstrapApplication(AppComponent, {
  providers: [provideHttpClient()] // "выдаём пропуск" курьеру HttpClient
});

Задание 2. CRUD в сервисе

Контекст. Теперь создадим «отдел доставки» — сервис TaskService, который умеет брать задачи, добавлять, менять и удалять их на сервере. В TaskFlow это список дел: каждая задача — отдельная запись.

Аналогия из жизни 💡 CRUD — это четыре действия с заказами в магазине:
GET — «покажите, что у вас есть» (получить список).
POST — «приняли новый заказ» (создать).
PATCH — «поменяйте в заказе одну строку» (обновить часть).
DELETE — «отмените заказ» (удалить).
Сам сервис — это курьер HttpClient, который везёт заказ.

Пошаговый разбор.

Что увидит студент. На экране опять ничего не появится — это пока только «инструкции для курьера». Но теперь у приложения есть готовый набор методов, чтобы управлять задачами.

@Injectable({ providedIn: 'root' }) // сервис доступен во всём приложении
export class TaskService {
  private http = inject(HttpClient); // наш "курьер"
  private base = 'http://localhost:3000/tasks'; // адрес склада задач

  // GET: "покажите все задачи"
  getAll()   { return this.http.get<Task[]>(this.base); }
  // POST: "создайте новую задачу" (d — данные новой задачи)
  create(d)  { return this.http.post<Task>(this.base, d); }
  // PATCH: "поменяйте задачу t" (меняем только переданные поля)
  update(t)  { return this.http.patch<Task>(`${this.base}/${t.id}`, t); }
  // DELETE: "удалите задачу с id"
  remove(id) { return this.http.delete<void>(`${this.base}/${id}`); }
}
Типизация. Укажите тип ответа (get<Task[]>), и Angular не даст обратиться к полям, которых нет в модели. Это как этикетка на посылке (поле: значение): вы сразу видите, что внутри, и не полезете за тем, чего там нет.

Задание 3. Обработка ошибок

Контекст. Сервер может лежать, интернет — отвалиться, а адрес — быть неправильным. Надо не уронить приложение, а аккуратно вернуть пустой список и дать понять, что что-то пошло не так.

Аналогия из жизни 💡 catchError — это сетка безопасности (ловит падение, чтобы не разбить программу). Если курьер споткнулся (сервер ответил ошибкой), он не падает вниз и не ломает всё шоу, а приземляется в сетку и возвращается с пустыми руками, но живой.

Пошаговый разбор.

Что увидит студент. Если сервер упал, приложение не «вылетит». Подписчик получит просто пустой массив, и экран останется рабочим.

getAll() {
  return this.http.get<Task[]>(this.base).pipe(
    catchError(err => {                 // сетка безопасности
      console.error('Ошибка загрузки:', err); // сообщаем разработчику
      return of<Task[]>([]); // пустой список вместо падения
    })
  );
}
Важно. Без catchError ошибка «убьёт» поток, и подписчик никогда не получит ни данных, ни ответа. Как будто курьер ушёл и не вернулся — приложение зависнет в ожидании.

Задание 4. Конечный автомат UI

Контекст. Компонент должен показать пользователю ровно одно из четырёх «состояний посылки». Это называется конечный автомат: в каждый момент времени экран находится в одном состоянии.

Аналогия из жизни 💡 Три (на самом деле четыре) состояния посылки:
loading — «посылка в пути», показываем «Загрузка…».
empty — «пришла, но коробка пустая», пишем «Задач нет».
success — «пришли ваши задачи», рисуем список.
error — «посылка потерялась», показываем ошибку.
Как в трекере заказов: нельзя одновременно и «в пути», и «доставлено».

Пошаговый разбор.

Что увидит студент. Пока грузится — «Загрузка…». Если пусто — «Задач нет». Если ошибка — красное сообщение. Если всё ок — список задач с галочками.

type State =
  | { status: 'loading' }                    // посылка в пути
  | { status: 'empty' }                      // пришла пустой
  | { status: 'success'; tasks: Task[] }     // пришли задачи
  | { status: 'error'; message: string };    // потерялась

// в шаблоне проверяем статус по очереди
@if (state().status === 'loading') {
  <p>Загрузка…</p>
} @else if (state().status === 'error') {
  <p>Ошибка: {{ state().message }}</p>
} @else if (state().status === 'empty') {
  <p>Задач нет</p>
} @else {
  @for (t of state().tasks; track t.id) { <app-task-item [task]="t" /> }
}
Поле задачи. Обратите внимание: у самой задачи есть поле completed (выполнена или нет) — это ДАННЫЕ задачи. Не путайте его с фильтром списка 'done': completed говорит «сделана ли эта одна задача», а 'done' — это значение фильтра, показывающее «только выполненные».

Интерактивная песочница

Контекст. Здесь — упрощённая версия тех же идей на чистом JavaScript и fetch (это «курьер» в обычном JS, без Angular). Вы можете нажать кнопку и увидеть все три состояния своими глазами.

Аналогия из жизни 💡 Песочница — это тренажёр магазина. Нажимаете «Загрузить» — курьер бежит на склад. Если склад работает — приносит товар (список). Если выключить json-server — склад закрыт, и курьер возвращается с «Ошибкой». Если склад пустой — приносит «Задач нет».

Пошаговый разбор.

▶️ Что увидит студент. При включённом json-server — список задач с галочками. При выключенном — красная строка «Ошибка: Failed to fetch». При пустой базе — «Задач нет (empty)».

Песочница: нажми «Запустить» — и увидишь список задач, «Задач нет» или «Ошибку» в зависимости от состояния сервера. Меняй код и запускай снова.
Песочница: loading / error / empty
💡 Попробуйте сами. Остановите json-server (Ctrl+C в терминале) и нажмите «Запустить» — увидите состояние error вживую. Это самый наглядный урок обработки ошибок.

Проверьте себя

Вопросы.
  1. Чем PATCH отличается от PUT? (Подсказка: PATCH меняет только часть заказа, PUT — заменяет весь.)
  2. Почему catchError возвращает of([]), а не просто []? (Подсказка: поток должен вернуть «посылку-обёртку», а не голые данные.)
  3. Какое состояние показать, если сервер недоступен? (Подсказка: посылка потерялась → error.)

Практика к уроку 7. Это последняя практическая работа занятия 2. На главную →

Куда дальше: теория HTTP — в Уроке 7; первый запрос с клика — в лабораторной №1.

📖 Глоссарий

ТерминЖизненная аналогияКоротко
HTTP-запросЗаказ в магазинеТы подходишь к прилавку и просишь товар (данные).
HTTP-статусОтвет продавца: 200 / 404 / 500200 — «есть», 404 — «нет такого», 500 — «сломался».
fetch / HttpClientКурьерВезёт твой заказ на склад и обратно с посылкой.
catchErrorСетка безопасностиЛовит падение запроса, чтобы не разбить программу.
JSONЭтикетка на посылкеПодпись на коробке: что внутри и как это прочитать.
loading / empty / errorТри состояния посылки«в пути» / «пустая коробка» / «потерялась».
signalГруппа в мессенджереПри изменении значения все подписчики получают сообщение.

🧠 Самопроверка

1. Если сервер недоступен, запрос выдаёт состояние success? Верно или неверно?

2. HttpClient — это сам сервер с данными? Верно или неверно?

3. JSON похож на этикетку на посылке? Верно или неверно?

Прогресс курса