Соберите экран списка задач из компонентов: standalone-компонент, входы, control flow и связывание классов.
В этом уроке мы строим экран списка дел (как в приложении «Заметки» или «Дела»). Представьте, что вы собираете машину: отдельные детали потом соединяются в одну работающую машину. Так же и в Angular — из маленьких «деталей» (компонентов) собирается весь экран.
input()@for с track и @empty@if и @switch[class.*]Создайте standalone-компонент, который получает задачу через вход и выводит её.
Контекст. В приложении TaskFlow каждая задача — это одна строчка списка. Чтобы не писать одно и то же много раз, мы делаем деталь-«однострочник» TaskItem и используем её для каждой задачи.
💡 Аналогия. Компонент — как деталь конструктора Lego. Вы собираете один кирпичик (компонент), а потом ставите его много раз, чтобы получился забор или дом. Вход input() — как почтовый ящик у этой детали: в него «приходит» письмо с данными (какая задача показать), а деталь сама решает, как это письмо отобразить.
Пошаговый разбор:
import { Component, input } — берём из Angular два инструмента: сам компонент и способ сделать вход.@Component({...}) — это «паспорт» детали: как её зовут (selector) и что она рисует (template).standalone: true — значит, компонент самодостаточный, ему не нужна отдельная «коробка-модуль», чтобы работать.task = input.required<Task>() — объявляем почтовый ящик: сюда обязательно должны положить задачу типа Task.{{ task().title }} — берём из письма заголовок и показываем на экране.Что увидит пользователь: на экране появится строчка вида «Изучить компоненты» (пока без оформления).
import { Component, input } from '@angular/core';
@Component({
selector: 'app-task-item', // имя детали: <app-task-item></app-task-item>
standalone: true, // компонент работает сам по себе
template: `
<li [class.done]="task().completed"> // табличка: если done — добавим класс
{{ task().title }} // покажем заголовок задачи
</li>
`
})
export class TaskItem {
task = input.required<Task>(); // вход-«почтовый ящик»: сюда придёт задача
}
done — через него можно зачеркнуть текст в CSS.Выведите задачи с ключом track и покажите заглушку, если список пуст.
💡 Аналогия. @for — как почтовый конвейер на складе: лента прокручивается, и для каждой посылки (задачи) работник выполняет одно и то же действие — берёт деталь TaskItem и кладёт её в чемодан. track — как бирка с номером на каждой посылке: по ней понятно, какая посылка которую по счёту, и если одну заменить, не перетряхивать весь конвейер.
Пошаговый разбор:
@for (task of tasks(); track task.id) — берём каждую задачу из списка tasks() по очереди.track task.id — говорим Angular: «различай задачи по их номеру id».<app-task-item [task]="task" />: для каждой задачи ставим деталь и кладём ей в почтовый ящик эту задачу.@empty { ... } — срабатывает только если список пустой (посылок на ленте нет).Что увидит пользователь: либо список строк задач, либо (если список пуст) надпись «Задач пока нет. Добавьте первую!».
@for (task of tasks(); track task.id) {
<app-task-item [task]="task" /> // для каждой задачи — своя деталь
} @empty {
<p>Задач пока нет. Добавьте первую!</p> // если список пуст
}
task.id понимает, какой элемент изменился, и обновляет только его, а не весь список. Это быстрее, особенно когда задач сотни.Покажите индикатор фильтра и разное состояние через управляющие конструкции.
💡 Аналогия. @if — как зонт: «если пошёл дождь (идёт загрузка) — раскрой зонт (покажи «Загрузка…»), иначе — спрячь». @switch — как переключатель света: выбрано одно из нескольких положений («все / открытые / закрытые»), и загорается только соответствующая надпись.
Пошаговый разбор:
@if (loading()) — проверяем, идёт ли загрузка. Если да — показываем «Загрузка…».@else — если загрузка закончилась, смотрим на фильтр.@switch (filter()) — берём текущее значение фильтра и выбираем подходящий @case.@case — это один вариант: 'all', 'active' или 'done' (слово done здесь — просто название фильтра «закрытые задачи», не путайте с полем задачи).Что увидит пользователь: пока данные грузятся — «Загрузка…»; потом — одну из надписей: «Все записи», «Открытые» или «Закрытые».
@if (loading()) {
<p>Загрузка…</p> // зонт раскрыт: идёт загрузка
} @else {
@switch (filter()) { // выбираем режим фильтра
@case ('all') { <p>Все записи</p> }
@case ('active') { <p>Открытые</p> }
@case ('done') { <p>Закрытые</p> } // done — значение фильтра
}
}
Добавьте CSS, чтобы выполненные задачи визуально отличались.
💡 Аналогия. Class binding ([class.done]) — как табличка на двери комнаты. Если задача выполнена (на двери табличка «занято/готово»), мы вешаем класс done, и CSS красит текст серым и зачёркивает. Нет таблички — текст обычный.
Пошаговый разбор:
done и внутри него — span (сам текст задачи).text-decoration: line-through — рисует линию поверх текста (зачёркивание).color: gray — делает текст серым, чтобы выполненное казалось «неактивным».Что увидит пользователь: выполненные задачи серые и перечёркнутые, невыполненные — чёрные и обычные.
.done span {
text-decoration: line-through; // зачёркиваем текст
color: gray; // делаем серым
}
[class.done]="task().completed" отличается от [ngClass]? (Подсказка: new control flow не требует отдельного импорта.)Здесь — та же логика на чистом JS, чтобы увидеть, как компонент «собирает» HTML из данных:
💡 Аналогия. Песочница — как макет из картона: вы не строите настоящий дом, а собираете игрушечный, чтобы понять принцип. Здесь TaskItem — это функция, которая по данным возвращает кусок HTML, а конвейер tasks.map(...) собирает все строчки вместе.
Что увидит пользователь в песочнице: три строчки. Первая — «✓ Изучить компоненты» — серая и зачёркнутая (выполнена). Остальные две — «○ Написать TaskItem» и «○ Настроить @for» — обычные.
[ ] отличается от ( ) в шаблоне? (Подсказка: квадратные скобки — «дай данные внутрь», круглые — «поймай событие снаружи».)track важен при больших списках? (Бирка с номером помогает не пересобирать весь список.)@if, а когда @switch? (@if — одно «да/нет», @switch — выбор из нескольких вариантов.)Практика к уроку 3. Далее: Практика к уроку 4 →
| Термин | Жизненная аналогия | Коротко |
|---|---|---|
| Компонент | Деталь конструктора Lego | Маленькая «запчасть» экрана, которую можно ставить много раз. |
| Шаблон | Форма-бланк | Разметка компонента: что и где показывать на экране. |
Интерполяция {{ }} | Вписывание имени в бланк | Вставляет значение переменной прямо в текст: {{ task().title }}. |
Вход input() | Почтовый ящик ребёнка | Через него компоненту «приходят» данные снаружи. |
| @if / @for / @switch | Зонт / почтовый конвейер / переключатель света | Условие «да/нет», повтор для каждого элемента, выбор из нескольких вариантов. |
Привязка классов [class.*] | Табличка на двери | Вешает CSS-класс, когда выполнено условие (напр. задача готова). |
1. Компонент Angular похож на деталь конструктора: один и тот же компонент можно использовать много раз на экране. Верно или неверно?
Ответ: Верно. Как кирпичик Lego ставят много раз, так и компонент TaskItem повторяют для каждой задачи.
2. Через input() компонент сам создаёт данные и отдаёт их родителю. Верно или неверно?
Ответ: Неверно. input() — это почтовый ящик: данные приходят в компонент извне, а не создаются им самим.
3. @switch удобнее, чем несколько @if, когда нужно выбрать один вариант из нескольких (например, фильтр «все / открытые / закрытые»). Верно или неверно?
Ответ: Верно. @switch — как переключатель света: выбрано одно положение, и загорается только соответствующая надпись.