Angular IDPStrong Junior
🔥 0

Модуль 2 · урок 3 з 15

Синтаксис шаблонів

Шаблон — це HTML із чотирма додатковими можливостями: вставити значення, передати дані в елемент, спіймати подію й керувати структурою. У сучасному Angular структурні конструкції стали частиною мови шаблонів, і це найпомітніша зміна порівняно з тим, що ти бачив у старих туторіалах.

Чотири види прив'язки

<h3>{{ task().title }}</h3>                    <!-- 1. інтерполяція: значення в текст -->

<input [value]="task().code">                  <!-- 2. властивість елемента -->
<div [class.is-overdue]="isLate()">            <!-- клас за умовою -->
<div [style.width.%]="progress()">             <!-- стиль з одиницями -->
<button [disabled]="saving()">                 <!-- атрибут-прапорець -->
<td [attr.colspan]="span()">                   <!-- саме атрибут, а не властивість -->

<button (click)="save()">                      <!-- 3. подія -->
<input (input)="onType($event)">               <!-- $event — об'єкт події -->
<form (submit)="submit()" (keydown.escape)="close()">  <!-- фільтр по клавіші -->

<app-search [(value)]="query">                 <!-- 4. в обидва боки -->
Властивість чи атрибут

[value] — це властивість DOM-об'єкта, [attr.value] — атрибут у HTML. Здебільшого потрібна перша форма. Друга — коли властивості просто не існує: colspan, aria-*, data-*. Якщо прив'язка «не працює без причини» — спробуй attr., це класична пастка.

Керування структурою: @if, @for, @switch

@if (loading()) {
  <app-skeleton />
} @else if (error()) {
  <p class="error">{{ error() }}</p>
} @else {
  <app-task-table [rows]="tasks()" />
}
@for (task of tasks(); track task.id) {
  <app-task-card [task]="task" />
} @empty {
  <p class="empty">Задач немає — саме час створити першу.</p>
}

Дві речі тут нові порівняно зі старим *ngFor:

  • track обов'язковий. Angular більше не дозволяє про нього забути — і це добре, бо саме він визначає, чи перемалює браузер увесь список, чи лише змінений рядок.
  • @empty вбудований. Раніше порожній стан доводилось писати окремим *ngIf — а найчастіше про нього просто забували (модуль 7 про це нагадає).
@switch (task().status) {
  @case ('done')    { <span class="badge badge--done">Готово</span> }
  @case ('blocked') { <span class="badge badge--blocked">Блок</span> }
  @default          { <span class="badge">В роботі</span> }
}

Чому track так важливий

TRACK TASK.ID — Є СТАБІЛЬНИЙ КЛЮЧ перемальовано лише один вставлений рядок БЕЗ СТАБІЛЬНОГО КЛЮЧА перебудовано весь список: втрачено фокус, скрол і стан вкладених компонентів На 20 рядках різниці не видно. На 1000 рядках таблиці з модуля 5 — це різниця між «швидко» і «лагає».
track каже Angular, як упізнати той самий елемент після оновлення масиву. Без нього кожне оновлення — це заново створені DOM-вузли.

@let — локальна змінна в шаблоні

@let user = task().assignee;
@let isLate = task().dueDate < today();

<p>{{ user?.name ?? 'Не призначено' }}</p>
<span [class.late]="isLate">{{ task().dueDate | date:'dd.MM' }}</span>

Раніше для цього робили трюк *ngIf="expr as value". Тепер є нормальні змінні — зручно, коли одне й те саме довге вираження повторюється в шаблоні кілька разів.

@defer — відкласти важке

@defer (on viewport) {
  <app-heavy-chart [data]="stats()" />
} @placeholder {
  <div class="chart-skeleton"></div>
} @loading (minimum 300ms) {
  <app-spinner />
} @error {
  <p>Не вдалось завантажити графік</p>
}

Код компонента всередині @defer потрапляє в окремий шматок бандла й вантажиться, лише коли блок з'явиться у видимій області. Тригери бувають різні: on viewport, on interaction, on hover, on timer(2s), when умова. Для дашборду з важким графіком це найдешевша оптимізація, яка існує.

Старий синтаксис: що ти зустрінеш у чужому коді

СтареНовеРізниця
*ngIf="x"@if (x) { }Не потребує імпорту, підтримує @else if
*ngFor="let t of list; trackBy: fn"@for (t of list; track t.id)track обов'язковий, є @empty
[ngSwitch]@switchЧитабельніше, без директив
*ngIf="x as v"@let v = x;Змінна без обгортання в умову
<ng-container>здебільшого не потрібенНові блоки не додають зайвого елемента самі

Старий синтаксис не «зламаний» і працює далі — на робочому проєкті ти майже напевно побачиш саме його. Але писати новий код варто в новому стилі, і на демо ліду це помітно.

Правило про логіку в шаблоні

У шаблоні не викликають методи: {{ calcTotal() }} виконується на кожній перевірці змін — інколи сотні разів за секунду. Замість цього computed() у класі або пайп. Це прямо пов'язано з модулем 3 і з вимогою про OnPush: метод у шаблоні зводить нанівець усю оптимізацію.

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