Structural directives (the ones with *, like *ngIf/) change the DOM by . The is syntactic sugar — under the hood, Angular wraps the element in an and the directive controls whether/how that template is rendered.
Structural directives (the ones with *, like *ngIf/) change the DOM by . The is syntactic sugar — under the hood, Angular wraps the element in an and the directive controls whether/how that template is rendered.
*ngFor*<ng-template><!-- what you write -->
<p *ngIf="isVisible">Hello</p>
<!-- what Angular actually does -->
<ng-template [ngIf]="isVisible">
<p>Hello</p>
</ng-template>
The * tells Angular: "wrap this element in a template, and let the directive decide when to stamp it into the DOM." An <ng-template> is an inert chunk of markup that isn't rendered until a directive instantiates it.
import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
@Directive({ selector: "[appUnless]" }) // *appUnless = "the opposite of ngIf"
export class UnlessDirective {
constructor(
private templateRef: TemplateRef<any>, // the wrapped <ng-template>
private viewContainer: ViewContainerRef, // where to render it
) {}
@Input() set appUnless(condition: boolean) {
if (!condition) {
this.viewContainer.createEmbeddedView(this.templateRef); // render it
} else {
this.viewContainer.clear(); // remove it
}
}
}
// usage: <p *appUnless="isHidden">Shown when isHidden is false</p>
The two injected pieces are the key: TemplateRef is the element's template (the "what to render"), and ViewContainerRef is the location in the DOM (the "where"). The directive calls createEmbeddedView to add the element or clear to remove it.
<!-- newer Angular replaces *ngIf/*ngFor with built-in block syntax -->
@if (isVisible) { <p>Hello</p> } @else { <p>Hidden</p> }
@for (item of items; track item.id) { <li>{{ item.name }}</li> }
This newer syntax is more readable and performant, though the underlying add/remove concept is the same.
Understanding the * → <ng-template> desugaring demystifies how *ngIf/*ngFor add and remove DOM, and lets you build custom structural directives (using TemplateRef + ViewContainerRef) for advanced template control — e.g. permission-based rendering, custom repeaters, or lazy stamping.
It also clarifies why you can't put two structural directives (*ngIf and *ngFor) on one element: each needs its own template wrapper.