Custom Structural Directive
Nima bu?
Structural directive DOM strukturasini o'zgartiradi. TemplateRef — ng-template ichidagi shablon havolasi; ViewContainerRef — shu joyga view (embedded view) yaratish/o'chirish konteyneri. createEmbeddedView() shablonni DOM'ga qo'shadi, clear() olib tashlaydi — *ngIf shu mexanizm asosida ishlaydi.
Kod misoli
import {
Directive,
Input,
TemplateRef,
ViewContainerRef,
inject,
} from '@angular/core';
@Directive({
selector: '[appUnless]',
standalone: true,
})
export class UnlessDirective {
private templateRef = inject(TemplateRef<unknown>);
private vcr = inject(ViewContainerRef);
private hasView = false;
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.vcr.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.vcr.clear();
this.hasView = false;
}
}
}
<p *appUnless="isHidden">Shart false bo'lsa ko'rinadi (ngIf teskari)</p>
<ng-template [appUnless]="isHidden">
<p>Property binding usuli ham ishlaydi</p>
</ng-template>
@Component({
selector: 'app-unless-demo',
standalone: true,
imports: [UnlessDirective],
templateUrl: './unless-demo.component.html',
})
export class UnlessDemoComponent {
isHidden = false;
}
Muhim nuance: createEmbeddedView chaqirilganda yaratilgan view change detection daraxtida qoladi — vcr.clear() chaqirilmaguncha memory'da turadi, shuning uchun ngOnDestroy'da tozalash yoki hasView kabi flag bilan boshqarish shart (aks holda bir xil view bir necha marta yaratilib, xotira sızishi va DOM dublikati yuzaga keladi).
Imtihonda
- TemplateRef nima va qayerdan keladi?
- ViewContainerRef qayerga inject qilinadi?
- Embedded view vs component view farqi?
createEmbeddedViewni shart tekshirmasdan har o'zgarishda chaqirsak nima bo'ladi (dublikat view muammosi)?
Yodlash uchun
Structural = TemplateRef + ViewContainerRef; createEmbeddedView / clear; hasView flag bilan dublikatni oldini ol.
Nima bu?
* prefiksi (*ngIf="x") Angular microsyntax — qisqa yozuv. U aslida ng-template ga aylantiriladi: [ngIf]="x" property va #ngIf="ngIf" template ref. Custom structural directive ham *appMyDir="expr; param: value" sintaksisini @Input() setter'lar orqali qabul qiladi.
Kod misoli
import {
Directive,
Input,
TemplateRef,
ViewContainerRef,
inject,
} from '@angular/core';
interface SelectContext<T> {
$implicit: T;
appSelect: T;
index: number;
}
@Directive({
selector: '[appSelect]',
standalone: true,
})
export class SelectDirective<T> {
private templateRef = inject(TemplateRef<SelectContext<T>>);
private vcr = inject(ViewContainerRef);
@Input() set appSelect(source: T[] | null) {
this.vcr.clear();
if (!source) return;
source.forEach((item, index) => {
this.vcr.createEmbeddedView(this.templateRef, {
$implicit: item,
appSelect: item,
index,
});
});
}
}
<!-- *appSelect="products" desugar qilinganda: -->
<ul>
<ng-template appSelect let-item let-i="index" [appSelect]="products">
<li>{{ i + 1 }}. {{ item.name }}</li>
</ng-template>
</ul>
<!-- Microsyntax qisqa yozuvi (bir xil) -->
<ul>
<li *appSelect="products; let item; let i = index">
{{ i + 1 }}. {{ item.name }}
</li>
</ul>
TypeScript strict template checking (strictTemplates: true) let-itemning tipini SelectContext<T>dan chiqara olmaydi, agar directive static ngTemplateContextGuard metodini e'lon qilmasa. Bu static metod compiler'ga context tipini "isbotlab beradi" — bo'lmasa item any yoki xato tipda qoladi va template ichida type error tutilmaydi.
static ngTemplateContextGuard<T>(
dir: SelectDirective<T>,
ctx: unknown
): ctx is SelectContext<T> {
return true;
}
Imtihonda
*ngFor="let x of items"desugar qanday ko'rinishga ega?$implicitvalet-xbog'lanishi?- Custom microsyntax parametr (
let-i="index") qanday ishlaydi? ngTemplateContextGuardnima uchun kerak va bo'lmasa nima yo'qoladi?
Yodlash uchun
*dir = ng-template dir; $implicit = default let-x qiymati; type-safe context uchun ngTemplateContextGuard.
