Компонент Angular и модель заявки
Кабинет «Диспетчер» помогает сотруднику видеть заявки на оборудование и помещения. В первой версии ему достаточно списка: проектор нужно заменить, Wi-Fi уже настраивают, переговорная подготовлена. Начнём с компонента Angular, который связывает эти данные с HTML. Вы получите маленькую страницу, а затем сможете узнать тот же список в конечном приложении. Сервер пока не нужен: предметные записи заданы в файле модели и не зависят от другого курса.
Понадобятся знания TypeScript: импорт модуля, интерфейс, массив объектов и метод класса. Профиль серии — Angular 22.0.0, TypeScript 6.0.3, RxJS 7.8.2, Node 22.22.3 или более новый выпуск ветки 22. Он выбран по официальной таблице совместимости, прочитанной 12 октября 2026 года. Архив не содержит lockfile; установка и компиляция не выполнялись. Поэтому далее мы описываем ожидаемое поведение, а не результаты запущенной программы.
Заявка как предметная запись
Заявка имеет устойчивый id, заголовок title, статус status, номер версии version и время обновления updatedAt. Устойчивый идентификатор отличает одну запись от другой даже после переименования. Версия понадобится позднее, чтобы два диспетчера не перезаписали изменения друг друга. Дата передаётся строкой ISO: это договор будущего HTTP API, а не экземпляр JavaScript Date. В первой главе два последних поля просто выводятся или остаются в модели.
Три статуса обозначим английскими значениями new, active, done, а русские подписи будем хранить отдельно. Такое разделение помогает менять формулировки интерфейса без изменения данных. Например, подпись «В работе» можно заменить на «Выполняется», сохранив значение active. Если использовать русскую подпись одновременно как ключ и как текст, перевод или редактура затронут сравнения и сохранение. Эта зависимость не видна в маленьком списке, но появляется при первом фильтре.
В конечном model.ts объявлены интерфейс Ticket, перечисление допустимых статусов и массив fixtures. Для начального компонента используем именно эти fixtures. Не создавайте вторую похожую коллекцию с другими идентификаторами: последующие главы продолжают rq-101, rq-102 и rq-103. Они представляют вымышленные данные лаборатории. Ни время, ни версия здесь не получены из рабочей службы заявок. Массив нужен для изучения устройства интерфейса и подготовки воспроизводимого договора.
Класс и шаблон
Ниже полный начальный файл src/app/app.ts. Он заменяет конечную оболочку App только для первого учебного состояния. src/main.ts по-прежнему передаёт App функции bootstrapApplication, а model.ts берётся из архива. Конечная конфигурация может уже включать Router и HttpClient; начальный компонент ими не пользуется. Не смешивайте этот листинг с отдельными строками конечного App: при замене используется весь файл.
import { Component } from '@angular/core';
import { fixtures, statusLabels } from './model';
@Component({selector:'app-root',template:`
<main><h1>Заявки</h1>
@for (ticket of tickets; track ticket.id) {
<article><h2>{{ ticket.title }}</h2>
<p>{{ ticket.id }} · {{ labels[ticket.status] }}</p>
</article>
}
</main>
`})
export class App { readonly tickets = fixtures; readonly labels = statusLabels; }
Декоратор Component описывает селектор и шаблон, а класс предоставляет значения для шаблона. Селектор app-root соответствует элементу в index.html. Вызов bootstrapApplication связывает этот корневой компонент со страницей. Angular создаёт его экземпляр и строит представление. HTML в строке template остаётся шаблоном Angular: выражение в двойных фигурных скобках читает поле класса, а блок @for повторяет участок разметки по коллекции. Обычная HTML-страница без Angular эти выражения не вычислит.
Ожидаемый результат — заголовок «Заявки» и три записи в исходном порядке. Для rq-101 появится «Заменить проектор» и подпись «Новая», для rq-102 — «Настроить Wi-Fi» и «В работе». Обратите внимание на labels[ticket.status]: модель содержит машинное значение, а пользователь получает понятный текст. Здесь не нужны запросы, подписки или обработчик загрузки. Если список пуст, это означает именно отсутствие локальных данных, а не медленное соединение.
Состав проекта и первая граница
Standalone-компонент в выбранном профиле можно использовать непосредственно; отдельный NgModule для этого примера не вводится. Когда появится дочерний компонент или директива RouterLink, они попадут в imports компонента. TypeScript import доставляет имя в модуль, а компонентный imports делает соответствующее средство доступным шаблону. Эти действия относятся к разным уровням. Забытый imports не исправляется наличием похожего HTML-тега: Angular должен знать, какой класс или директиву применять.
Оформление вынесено в src/styles.css: шрифт, ширина контейнера, отступы и видимый фокус пригодятся всему кабинету. Не привязывайте смысл статуса только к цвету карточки. Уже сейчас текстовая подпись сообщает состояние человеку, который не различает выбранные цвета. Заголовок заявки располагается внутри h2, а название страницы — в h1. Так структура документа соответствует структуре информации, хотя первая версия ещё не содержит навигации и редактора.
Проверьте пример мысленно на изменении данных: если заменить title у rq-101 до создания страницы, ожидается другой заголовок при том же id. Если добавить четвёртый объект с новым id, цикл должен создать четвёртую карточку. А повтор id нарушит договор идентичности элементов. Именно поэтому идентификатор не равен номеру позиции. Перестановка массива меняет расположение заявок, но не превращает заявку на проектор в заявку на Wi-Fi.
Архив включает одну полную конечную версию кабинета и README с читательскими действиями запуска. На этом шаге не нужно разбирать каждый файл: сначала проследите цепочку index.html → main.ts → App → fixtures. Позднее компонент получит изменяемое состояние, затем дочернюю строку, адреса и HTTP-границу. Нынешний результат ограничен статическим отображением исходной модели. Запись в базу, совместная работа и безопасный вход из этого списка пока не следуют. Перед первым чтением файла сравните model.ts и index.html. Модель отвечает за вымышленные заявки, а index — за документ, в котором появляется корневой элемент. Перенос массива внутрь HTML не сделал бы данные независимыми от шаблона, а перенос base href в Ticket не имел бы предметного смысла. Эти разные уровни станут заметны после маршрутизации, когда оболочка страницы сохранится, а данные и представление отдельного экрана изменятся. Уже сейчас полезно читать структуру по назначению, а не только по порядку файлов в папке.
Статус примера — not_run. Зависимости, приложение, SQL, HTTP и тесты не запускались. Описанные ответы и изменения интерфейса ожидаемые; чтение документации не является проверкой выполнением. Полная конечная версия находится в архиве исходников, а её ограничения и действия читателя — в README и CONTRACT.md.
Перейдите к оглавлению серии или общему маршруту интерактивной веб-разработки. Механизм этой главы сверяется с официальным первичным источником; конкретные версии и непроверенные части перечислены в договоре курса.