Custom Structural Directives
Custom structural directives let you create your own DOM manipulation logic.
Structural directives add, remove, or move DOM elements. Built-in examples include @if and @for โ but you can build your own!
"Structural = bulldozer โ pura element add ya remove kar deta hai." Unlike attribute directives that just change appearance, structural directives control whether the element even exists in the DOM.
Use cases: show content only to admins, delay content appearance, repeat content with custom logic, conditionally render based on permissions.
Custom structural directives rely on two key services:
- TemplateRef โ Reference to the
<ng-template>Angular creates from your content. It's like a blueprint of what to render. - ViewContainerRef โ The container where you can insert or remove views. It's the "wall" where you hang the blueprint.
constructor(private templateRef: TemplateRef<any>,\n private viewContainerRef: ViewContainerRef) {}
"TemplateRef = drawing blueprint, ViewContainerRef = wall where you hang it."
To create a view: this.viewContainerRef.createEmbeddedView(this.templateRef)
To remove all views: this.viewContainerRef.clear()
Let's build appUnless โ the opposite of @if:
@Directive({\n selector: '[appUnless]',\n standalone: true\n})\nexport class UnlessDirective {\n constructor(private templateRef: TemplateRef<any>,\n private viewContainerRef: ViewContainerRef) {}
@Input() set appUnless(condition: boolean) {\n if (!condition) {\n // Show content when condition is FALSE\n this.viewContainerRef.createEmbeddedView(this.templateRef);\n } else {\n // Hide content when condition is TRUE\n this.viewContainerRef.clear();\n }\n }\n}
Usage: <p *appUnless="isHidden">Shows when isHidden is false</p>
"appUnless = ulta @if โ false pe dikhata hai, true pe chupata hai."
Note: We use a setter (@Input() set) instead of a simple property. The setter runs every time the input value changes, so the DOM stays in sync.
Let's build appDelay โ shows content after a delay:
@Directive({\n selector: '[appDelay]',\n standalone: true\n})\nexport class DelayDirective {\n constructor(private templateRef: TemplateRef<any>,\n private viewContainerRef: ViewContainerRef) {}
@Input() set appDelay(delayMs: number) {\n setTimeout(() => {\n this.viewContainerRef.createEmbeddedView(this.templateRef);\n }, delayMs);\n }\n}
Usage: <div *appDelay="3000">Appears after 3 seconds</div>
"Delay = biryani dum โ thoda ruko, phir dikhaata hai."
Important: For setTimeout, remember to clean up in ngOnDestroy to prevent memory leaks if the component is destroyed before the delay completes.
The * (asterisk) prefix in *appUnless is syntactic sugar:
<!-- This: -->\n<div *appUnless="isHidden">Shown when !isHidden</div>\n\n<!-- Becomes this: -->\n<ng-template [appUnless]="isHidden">\n <div>Shown when !isHidden</div>\n</ng-template>
"Star = shortcut โ Angular samajh jaata hai tum kya chahte ho."
The * syntax automatically wraps your element in an <ng-template> and binds the directive to it. This is why you inject TemplateRef โ Angular creates it from the * expansion.
Note: The new Angular 17+ @if/@for syntax does NOT use * expansion. It uses native block syntax that doesn't need ng-template.
Key Takeaways
- โ Custom structural directives use TemplateRef (blueprint) and ViewContainerRef (wall).
- โ TemplateRef holds the content template; ViewContainerRef inserts/removes it from DOM.
- โ appUnless shows content when condition is false โ opposite of @if.
- โ appDelay shows content after a configurable delay using setTimeout.
- โ The * syntax is sugar for ng-template wrapping.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login