Performance: Custom structural directives
Custom structural directives affect how Angular manipulates the DOM structure, impacting rendering speed and layout stability.
Jump into concepts and practice - no test required
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appGoodIf]' }) export class GoodIfDirective { private hasView = false; constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} set appGoodIf(condition: boolean) { if (condition && !this.hasView) { this.viewContainer.createEmbeddedView(this.templateRef); this.hasView = true; } else if (!condition && this.hasView) { this.viewContainer.clear(); this.hasView = false; } } }
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appBadIf]' }) export class BadIfDirective { constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {} set appBadIf(condition: boolean) { this.viewContainer.clear(); if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } } }
| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Clearing and recreating views on every condition change | Multiple clears and creates | Triggers 1 reflow per change | High paint cost due to layout shifts | [X] Bad |
| Tracking view state and updating only on change | Minimal DOM operations | Reflows only on actual DOM changes | Lower paint cost with stable layout | [OK] Good |
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();
}
}
}<div *appShowIf="false">Hello World</div>
appShowIf is false, viewContainer.clear() removes any embedded views, so nothing is rendered.<div> with 'Hello World' is inside the template controlled by the directive, so it won't appear if condition is false.@Directive({ selector: '[appIf]' })
export class IfDirective {
constructor(private templateRef: TemplateRef<any>) {}
@Input() set appIf(condition: boolean) {
if (condition) {
this.templateRef.createEmbeddedView();
}
}
}createEmbeddedView() on TemplateRef alone is invalid; it should be called on ViewContainerRef with TemplateRef as argument.*appUnless that shows content only when a condition is false. Which implementation correctly achieves this behavior?*appUnless should show content only when the condition is false, so the view is created when !condition.if (!condition) to create the embedded view and clearing it otherwise matches the requirement.if (!condition) to create the embedded view, else clear it -> Option A