Angular 17+ yangi sintaksis
Angular 17 built-in control flow sintaksisini kiritdi: @if, @for, @switch, @defer. Structural directive (*ngIf, *ngFor) o'rniga to'g'ridan-to'g'ri template sintaksisi — o'qish osonroq, build vaqtida optimallashtiriladi va TypeScript bilan yaxshiroq integratsiya.
@if / @else if / @else — *ngIf o'rniga
Nima bu?
@if shartli render qiladi; @else if va @else zanjirli shartlar. as kalit so'zi bilan shart natijasini lokal o'zgaruvchi sifatida ishlatish mumkin (@if (user(); as u)). *ngIf dan farqi — alohida NgIf import kerak emas, nested shartlar o'qish oson.
Kod misoli
import { Component, signal } from '@angular/core';
type AuthState = 'loading' | 'guest' | 'authenticated';
@Component({
selector: 'app-auth-banner',
standalone: true,
template: `
@if (authState() === 'loading') {
<p class="skeleton">Yuklanmoqda...</p>
} @else if (authState() === 'guest') {
<a routerLink="/login">Kirish</a>
} @else {
@if (user(); as u) {
<p>Salom, {{ u.name }}!</p>
@if (u.role === 'admin') {
<a routerLink="/admin">Admin</a>
}
}
}
`,
})
export class AuthBannerComponent {
authState = signal<AuthState>('loading');
user = signal<{ name: string; role: string } | null>(null);
ngOnInit(): void {
setTimeout(() => {
this.authState.set('authenticated');
this.user.set({ name: 'Dilnoza', role: 'admin' });
}, 500);
}
}
<!-- Klassik *ngIf (hali qo'llab-quvvatlanadi) -->
<div *ngIf="items.length; else empty">...</div>
<ng-template #empty>Ro'yxat bo'sh</ng-template>
<!-- Zamonaviy -->
@if (items.length) {
<ul>...</ul>
} @else {
<p>Ro'yxat bo'sh</p>
}
Imtihonda
@if (expr; as x)sintaksisi nima qiladi?*ngIfdan@ifga migratsiya qanday amalga oshiriladi (ng generate @angular/core:control-flow)?@else ifnechta bo'lishi mumkin?
Yodlash uchun
@if / @else if / @else — *ngIf o'rniga; as — lokal alias.
@for ... track ... @empty — *ngFor o'rniga
Nima bu?
@for (item of items; track item.id) ro'yxatni render qiladi. track majburiy — DOM node'larni qayta ishlatish uchun unique identifier. @empty — ro'yxat bo'sh bo'lganda ko'rsatiladi. $index, $first, $last, $even, $odd kontekst o'zgaruvchilari mavjud.
Kod misoli
interface Todo {
id: number;
title: string;
done: boolean;
}
@Component({
selector: 'app-todo-list',
standalone: true,
template: `
<ul>
@for (todo of todos(); track todo.id; let i = $index) {
<li [class.done]="todo.done">
<span>{{ i + 1 }}. {{ todo.title }}</span>
<button type="button" (click)="toggle(todo.id)">
{{ todo.done ? 'Bekor' : 'Bajarildi' }}
</button>
</li>
} @empty {
<li class="empty">Vazifalar yo'q — dam oling ☕</li>
}
</ul>
`,
styles: [`.done { text-decoration: line-through; opacity: 0.6; }`],
})
export class TodoListComponent {
todos = signal<Todo[]>([
{ id: 1, title: 'Angular @for o\'rganish', done: false },
{ id: 2, title: 'track ifodasini tushunish', done: true },
]);
toggle(id: number): void {
this.todos.update((list) =>
list.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
}
}
// track expression variantlari
@for (user of users(); track user.id) { ... }
@for (item of items(); track $index) { ... } // faqat statik ro'yxat uchun
@for (row of rows(); track row.id + row.version) { ... } // composite key
Imtihonda
- Nima uchun
trackmajburiy? trackBy(klassik) vatrack(yangi) farqi?@emptyqachon render bo'ladi?
Yodlash uchun
@for ... track id — DOM reuse; @empty — bo'sh holat.
@switch / @case / @default
Nima bu?
@switch (expr) — qiymatga qarab turli bloklarni render qiladi. @case value — aniq mos kelish; @default — hech biri mos kelmasa. [ngSwitch] / *ngSwitchCase o'rniga sodda sintaksis. Strict equality (===) bilan solishtiriladi.
Kod misoli
type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
@Component({
selector: 'app-order-status',
standalone: true,
template: `
@switch (status()) {
@case ('pending') {
<span class="badge badge-warn">Kutilmoqda</span>
}
@case ('processing') {
<span class="badge badge-info">Tayyorlanmoqda</span>
}
@case ('shipped') {
<span class="badge badge-primary">Yo'lda</span>
<p>Tracking: {{ trackingNumber() }}</p>
}
@case ('delivered') {
<span class="badge badge-success">Yetkazildi ✓</span>
}
@default {
<span class="badge badge-error">Noma'lum holat</span>
}
}
`,
})
export class OrderStatusComponent {
status = signal<OrderStatus>('shipped');
trackingNumber = signal('TRK-8842-UZ');
}
<!-- Klassik ngSwitch -->
<div [ngSwitch]="status">
<p *ngSwitchCase="'pending'">Kutilmoqda</p>
<p *ngSwitchDefault>Boshqa</p>
</div>
Imtihonda
@switchqaysi taqqoslashni ishlatadi?- Bir nechta
@casebir xil blokni qanday ulashadi? @switchva@if/@else ifqachon tanlanadi?
Yodlash uchun
@switch → @case / @default; === taqqoslash.
@defer — lazy block (on viewport, on interaction)
Nima bu?
@defer blokni lazy yuklaydi — komponent, pipe yoki og'ir template qismi dastlab bundle'ga kirmaydi. Trigger'lar: on idle (browser bo'sh), on viewport (element ko'rinsa), on interaction (click/hover), on hover, on immediate, on timer(2s), when condition. Performance va initial load uchun kuchli vosita.
SSR bilan o'zaro ta'sir: server render qilinganda @defer bloki odatda faqat @placeholderni HTML'ga chiqaradi — asosiy kontent server javobida yo'q. on viewport, on interaction, on hover kabi trigger'lar brauzer/DOM API'ga tayanadi, shuning uchun serverda ishlamaydi — ular faqat client hydration tugagach faollashadi. Bu degani: agar SEO yoki LCP uchun muhim kontent @defer ichiga qo'yilsa, u qidiruv tizimlari va birinchi render'da ko'rinmaydi — noto'g'ri joyda ishlatilsa Core Web Vitals'ni yomonlashtirishi mumkin. Keyingi Angular versiyalarida qo'shilgan incremental hydration bu muammoni qisman yumshatadi — server ayrim defer bloklarini ham oldindan render qilishi mumkin, lekin bu alohida, ixtiyoriy sozlanadigan xususiyat.
Kod misoli
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
standalone: true,
template: `
<h1>Dashboard</h1>
<!-- Viewport'ga kirganda yuklash -->
@defer (on viewport) {
<app-heavy-chart [data]="chartData" />
} @placeholder {
<div class="chart-skeleton">Grafik joyi...</div>
} @loading (minimum 300ms) {
<p>Grafik yuklanmoqda...</p>
}
<!-- Foydalanuvchi bosganda yuklash -->
@defer (on interaction) {
<app-admin-panel />
} @placeholder {
<button type="button">Admin panelni ochish</button>
}
<!-- Shart bajarilganda -->
@defer (when isPremium()) {
<app-premium-features />
} @placeholder {
<p>Premium rejimga o'ting</p>
}
`,
})
export class DashboardComponent {
chartData = [{ label: 'Yan', value: 42 }];
isPremium = () => false;
}
// Alohida fayl — lazy import
@Component({
selector: 'app-heavy-chart',
standalone: true,
template: `<canvas #chart></canvas>`,
})
export class HeavyChartComponent {
@Input({ required: true }) data!: unknown[];
}
Imtihonda
@defer (on viewport)va lazy route farqi?@loading (minimum 300ms)nima uchun kerak?@deferqaysi holatlarda bundle size'ni kamaytiradi?- SSR'da
@deferichidagi kontent boshlang'ich HTML javobida bo'ladimi?
Yodlash uchun
@defer — lazy block; trigger: viewport, interaction, when, timer.
@placeholder, @loading, @error — defer bloklar
Nima bu?
@defer bilan birga uchta yordamchi blok: @placeholder — yuklanishdan oldin (skeleton, tugma); @loading — yuklanayotganda (spinner); @error — xato bo'lganda (fallback UI). @loading (minimum Xms) — tez yuklanishda miltillashni oldini oladi. @error ichida qayta urinish tugmasi qo'yish mumkin.
Kod misoli
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-defer-demo',
standalone: true,
template: `
@defer (on viewport) {
<app-remote-widget [config]="widgetConfig()" />
} @placeholder (minimum 500ms) {
<div class="skeleton skeleton-widget" aria-hidden="true"></div>
} @loading (after 100ms; minimum 400ms) {
<div class="spinner" role="status">Widget yuklanmoqda...</div>
} @error {
<div class="error-panel">
<p>Widget yuklanmadi.</p>
<button type="button" (click)="retry()">Qayta urinish</button>
</div>
}
`,
})
export class DeferDemoComponent {
widgetConfig = signal({ theme: 'dark' });
private retryCount = 0;
retry(): void {
this.retryCount++;
// Defer qayta trigger — config reference o'zgartirish
this.widgetConfig.set({ theme: this.retryCount % 2 ? 'light' : 'dark' });
}
}
// @error — dynamic import xatosi
@Component({
selector: 'app-remote-widget',
standalone: true,
template: `<p>Remote widget ishlayapti</p>`,
})
export class RemoteWidgetComponent {
@Input({ required: true }) config!: { theme: string };
constructor() {
// Simulyatsiya: ba'zi holatlarda xato
if (Math.random() < 0.01) {
throw new Error('Network failure');
}
}
}
Imtihonda
@placeholder (minimum 500ms)va@loading (after 100ms)farqi?@errorqachon ishga tushadi?- Defer bloklar tartibi va ixtiyoriy/emasligi?
Yodlash uchun
placeholder → oldin; loading → yuklanish; error → xato fallback.
