Custom structural directives let you change the layout by adding or removing parts of the page. They help you create reusable and dynamic HTML structures.
Custom structural directives in Angular
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
Angular
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[appMyDirective]' }) export class MyDirective { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef ) {} @Input() set appMyDirective(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }
TemplateRef is the HTML block you want to control.
ViewContainerRef is where Angular inserts or removes that block.
Examples
Angular
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[appShowIf]' }) export class ShowIfDirective { constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} @Input() set appShowIf(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }
Angular
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core'; @Directive({ selector: '[appRepeat]' }) export class RepeatDirective { constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} @Input() set appRepeat(times: number) { this.viewContainer.clear(); for (let i = 0; i < times; i++) { this.viewContainer.createEmbeddedView(this.templateRef); } } }
Sample Program
This example creates a custom directive appShowIf that works like *ngIf. Clicking the button toggles the message on and off.
Angular
import { Component, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appShowIf]' }) export class ShowIfDirective { constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} @Input() set appShowIf(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } } @Component({ selector: 'app-root', template: ` <button (click)="toggle()">Toggle Message</button> <p *appShowIf="show">Hello! This message is controlled by a custom structural directive.</p> ` }) export class AppComponent { show = true; toggle() { this.show = !this.show; } }
Important Notes
Always prefix your directive selectors to avoid conflicts.
Structural directives must use TemplateRef and ViewContainerRef to control DOM.
Use @Input() setter to react to changes in directive input.
Summary
Custom structural directives let you add or remove HTML blocks dynamically.
They use TemplateRef and ViewContainerRef to control the page layout.
You create them by writing a directive with an input property that controls when to show or hide content.
Practice
1. What is the main purpose of a custom structural directive in Angular?
easy
Solution
Step 1: Understand structural directives role
Structural directives change the structure of the DOM by adding or removing elements.Step 2: Identify the main use case
Custom structural directives let you control when parts of the page appear or disappear dynamically.Final Answer:
To add or remove HTML elements dynamically based on conditions -> Option DQuick Check:
Structural directives = dynamic HTML blocks [OK]
Hint: Structural directives control HTML blocks, not styles or events [OK]
Common Mistakes:
- Confusing structural directives with attribute directives
- Thinking they handle styling or events
- Assuming they fetch data
2. Which of the following is the correct way to inject dependencies in a custom structural directive constructor?
easy
Solution
Step 1: Identify dependencies for structural directives
Structural directives need TemplateRef to access the template and ViewContainerRef to insert or remove views.Step 2: Match constructor parameters
constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} correctly injects TemplateRef and ViewContainerRef, which are essential for custom structural directives.Final Answer:
constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} -> Option CQuick Check:
TemplateRef + ViewContainerRef = constructor params [OK]
Hint: Use TemplateRef and ViewContainerRef in constructor for structural directives [OK]
Common Mistakes:
- Injecting ElementRef or Renderer2 which are for attribute directives
- Injecting unrelated services like HttpClient or Router
- Missing TemplateRef or ViewContainerRef
3. Given this directive code snippet, what will be the rendered output if
Usage:
appShowIf input is false?@Directive({ selector: '[appShowIf]' })
export class ShowIfDirective {
constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {}
@Input() set appShowIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}Usage:
<div *appShowIf="false">Hello World</div>
medium
Solution
Step 1: Analyze the directive behavior when condition is false
WhenappShowIfis false,viewContainer.clear()removes any embedded views, so nothing is rendered.Step 2: Understand the usage effect
The<div>with 'Hello World' is inside the template controlled by the directive, so it won't appear if condition is false.Final Answer:
Nothing will be rendered inside the div -> Option AQuick Check:
False condition = no content shown [OK]
Hint: False input clears view container, so no content appears [OK]
Common Mistakes:
- Thinking the div still renders empty
- Assuming an error occurs
- Confusing attribute directives with structural directives
4. Identify the error in this custom structural directive code:
@Directive({ selector: '[appIf]' })
export class IfDirective {
constructor(private templateRef: TemplateRef<any>) {}
@Input() set appIf(condition: boolean) {
if (condition) {
this.templateRef.createEmbeddedView();
}
}
}medium
Solution
Step 1: Check constructor dependencies
The directive injects only TemplateRef but misses ViewContainerRef, which is needed to insert or clear views.Step 2: Analyze method usage
CallingcreateEmbeddedView()on TemplateRef alone is invalid; it should be called on ViewContainerRef with TemplateRef as argument.Final Answer:
Missing ViewContainerRef injection and usage to insert the view -> Option BQuick Check:
ViewContainerRef required to add views [OK]
Hint: Always inject ViewContainerRef to add or remove views [OK]
Common Mistakes:
- Trying to create views directly from TemplateRef
- Forgetting to inject ViewContainerRef
- Misnaming input properties
5. You want to create a custom structural directive
*appUnless that shows content only when a condition is false. Which implementation correctly achieves this behavior?hard
Solution
Step 1: Understand the directive goal
*appUnlessshould show content only when the condition is false, so the view is created when!condition.Step 2: Match logic to code
Usingif (!condition)to create the embedded view and clearing it otherwise matches the requirement.Final Answer:
Useif (!condition)to create the embedded view, else clear it -> Option AQuick Check:
Show content when false = if (!condition) create view [OK]
Hint: Invert condition logic to show content only when false [OK]
Common Mistakes:
- Using if (condition) instead of if (!condition)
- Not clearing the view when condition is true
- Creating view unconditionally
