liveBook · Manning

Structural directive

As explained in Modern Angular

A structural directive is an Angular directive that changes a template's structure by creating, removing, or transforming embedded views.

A structural directive changes template structure by creating, removing, or transforming embedded views rather than merely changing a host element’s behavior. Angular uses this pattern for conditional UI and dynamic component composition. section 10.1.1 section 4.1.4

Use structural directives to:

  • Render content conditionally, replacing nested *ngIf and ng-template chains with Angular’s v17 @if and @else syntax when appropriate. section 10.1.1
  • Create an embedded view from TemplateRef, place it through ViewContainerRef, and dynamically project its nodes into a component. section 4.1.4
  • Reduce repetitive enterprise templates, such as deeply nested loading components, while preserving component inputs and lifecycle-aware updates. section 4.1.4

A loading directive for projected content

A loader directive injects TemplateRef and ViewContainerRef, creates the original template as an embedded view, dynamically creates LoaderComponent, and projects the view’s root nodes into it. Calling setInput updates loading through Angular’s input and lifecycle machinery instead of assigning the component instance property directly. section 4.1.4

Listing 4.9 Directive that dynamically renders projected content
@Directive({
  selector: '[loading]',
  standalone: true,
})
export class LoaderDirective implements OnInit, OnChanges {
  private readonly templateRef = inject(TemplateRef);  #1
  private readonly vcRef = inject(ViewContainerRef);  #2
  @Input() loading = false;
  templateView: EmbeddedViewRef<any>;
  loaderRef: ComponentRef<LoaderComponent>;  #3

  ngOnInit() {
    this.templateView = this.templateRef.createEmbeddedView({});
    this.loaderRef = this.vcRef.createComponent(
      LoaderComponent,  #4
      { 
        injector: this.vcRef.injector,
        projectableNodes: [this.templateView.rootNodes],  #5
      },
  );

    this.loaderRef.setInput('loading', this.loading);  #6
  }

  ngOnChanges() {
    this.loaderRef?.setInput('loading', this.loading);
  }
}

Inspect the complete directive implementation, including embedded-view creation, projected nodes, dynamic component creation, and changing the loading input. section 4.1.4

How Modern Angular covers it

How deep it goes
Explains embedded-view creation, component projection, input updates, and lifecycle-aware changes, while deferring broader directive fundamentals.
Examples
Uses nested loading overlays, a custom loading directive, *ngIf and *ngFor, and @if conditional notification and project-card templates.
What it assumes
Assumes familiarity with Angular templates, components, inputs, standalone imports, and lifecycle hooks; teaches dynamic view wiring through implementation.
What you can do after
Build it from scratch as a standalone directive that creates embedded views, renders dynamic components, projects content, and updates inputs.
How it connects
Connects structural directives with embedded views, TemplateRef, ViewContainerRef, dynamic components, content projection, lifecycle hooks, and change detection.

Also explained in

Compare how 2 books explain structural directive →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern Angular. The section links point to the parts of the book it draws on.