Chapter 4.2โ˜• 16 min read

Custom Structural Directives

Custom structural directives let you create your own DOM manipulation logic.

01What is a Custom Structural Directive

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.

02The TemplateRef and ViewContainerRef

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()

03Building appUnless Directive

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.

04Building appDelay Directive

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.

05Structural Directive Shorthand (*)

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.
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase