In-depth Guides
Directives

Structural directives

Structural directives are directives applied to an <ng-template> element that conditionally or repeatedly render the content of that <ng-template>.

Example use case

In this guide you'll build a structural directive which fetches data from a given data source and renders its template when that data is available. This directive is called SelectDirective, after the SQL keyword SELECT, and match it with an attribute selector [select].

SelectDirective will have an input naming the data source to be used, which you will call selectFrom. The select prefix for this input is important for the shorthand syntax. The directive will instantiate its <ng-template> with a template context providing the selected data.

The following is an example of using this directive directly on an <ng-template> would look like:

      
<ng-template select let-data [selectFrom]="source">  <p>The data is: {{ data }}</p></ng-template>

The structural directive can wait for the data to become available and then render its <ng-template>.

HELPFUL: Note that Angular's <ng-template> element defines a template that doesn't render anything by default, if you just wrap elements in an <ng-template> without applying a structural directive those elements will not be rendered.

For more information, see the ng-template API documentation.

Structural directive shorthand

Angular supports a shorthand syntax for structural directives which avoids the need to explicitly author an <ng-template> element.

Structural directives can be applied directly on an element by prefixing the directive attribute selector with an asterisk (*), such as *select. Angular transforms the asterisk in front of a structural directive into an <ng-template> that hosts the directive and surrounds the element and its descendants.

You can use this with SelectDirective as follows:

      
<p *select="let data from source">The data is: {{data}}</p>

This example shows the flexibility of structural directive shorthand syntax, which is sometimes called microsyntax.

When used in this way, only the structural directive and its bindings are applied to the <ng-template>. Any other attributes or bindings on the <p> tag are left alone. For example, these two forms are equivalent:

      
<!-- Shorthand syntax: --><p class="data-view" *select="let data from source">The data is: {{data}}</p><!-- Long-form syntax: --><ng-template select let-data [selectFrom]="source">  <p class="data-view">The data is: {{data}}</p></ng-template>

Shorthand syntax is expanded through a set of conventions. A more thorough grammar is defined below, but in the above example, this transformation can be explained as follows:

The first part of the *select expression is let data, which declares a template variable data. Since no assignment follows, the template variable is bound to the template context property $implicit.

The second piece of syntax is a key-expression pair, from source. from is a binding key and source is a regular template expression. Binding keys are mapped to properties by transforming them to PascalCase and prepending the structural directive selector. The from key is mapped to selectFrom, which is then bound to the expression source. This is why many structural directives will have inputs that are all prefixed with the structural directive's selector.

One structural directive per element

You can only apply one structural directive per element when using the shorthand syntax. This is because there is only one <ng-template> element onto which that directive gets unwrapped. Multiple directives would require multiple nested <ng-template>, and it's unclear which directive should be first. <ng-container> can be used when to create wrapper layers when multiple structural directives need to be applied around the same physical DOM element or component, which allows the user to define the nested structure.

Creating a structural directive

This section guides you through creating the SelectDirective.

  1. Generate the directive

    Using the Angular CLI, run the following command, where select is the name of the directive:

          
    ng generate directive select

    Angular creates the directive class and specifies the CSS selector, [select], that identifies the directive in a template.

  2. Make the directive structural

    Import TemplateRef, and ViewContainerRef. Inject TemplateRef and ViewContainerRef in the directive constructor as private variables.

          
    import {Directive, TemplateRef, ViewContainerRef} from '@angular/core';@Directive({  selector: '[select]',})export class SelectDirective {  constructor(private templateRef: TemplateRef, private ViewContainerRef: ViewContainerRef) {}}
  3. Add the 'selectFrom' input

    Add a selectFrom @Input() property.

          
    export class SelectDirective {  // ...  @Input({required: true}) selectFrom!: DataSource;}
  4. Add the business logic

    With SelectDirective now scaffolded as a structural directive with its input, you can now add the logic to fetch the data and render the template with it:

          
    export class SelectDirective {  // ...  async ngOnInit() {    const data = await this.selectFrom.load();    this.viewContainerRef.createEmbeddedView(this.templateRef, {      // Create the embedded view with a context object that contains      // the data via the key `$implicit`.      $implicit: data,    });  }}

That's it - SelectDirective is up and running. A follow-up step might be to add template type-checking support.

Structural directive syntax reference

When you write your own structural directives, use the following syntax:

      
*:prefix="( :let | :expression ) (';' | ',')? ( :let | :as | :keyExp )*"

The following patterns describe each portion of the structural directive grammar:

      
as = :export "as" :local ";"?keyExp = :key ":"? :expression ("as" :local)? ";"?let = "let" :local "=" :export ";"?
Keyword Details
prefix HTML attribute key
key HTML attribute key
local Local variable name used in the template
export Value exported by the directive under a given name
expression Standard Angular expression

How Angular translates shorthand

Angular translates structural directive shorthand into the normal binding syntax as follows:

Shorthand Translation
prefix and naked expression [prefix]="expression"
keyExp [prefixKey]="expression" (The prefix is added to the key)
let local let-local="export"

Shorthand examples

The following table provides shorthand examples:

Shorthand How Angular interprets the syntax
*ngFor="let item of [1,2,3]" <ng-template ngFor let-item [ngForOf]="[1, 2, 3]">
*ngFor="let item of [1,2,3] as items; trackBy: myTrack; index as i" <ng-template ngFor let-item [ngForOf]="[1,2,3]" let-items="ngForOf" [ngForTrackBy]="myTrack" let-i="index">
*ngIf="exp" <ng-template [ngIf]="exp">
*ngIf="exp as value" <ng-template [ngIf]="exp" let-value="ngIf">

Improving template type checking for custom directives

You can improve template type checking for custom directives by adding template guards to your directive definition. These guards help the Angular template type checker find mistakes in the template at compile time, which can avoid runtime errors. Two different types of guards are possible:

  • ngTemplateGuard_(input) lets you control how an input expression should be narrowed based on the type of a specific input.
  • ngTemplateContextGuard is used to determine the type of the context object for the template, based on the type of the directive itself.

This section provides examples of both kinds of guards. For more information, see Template type checking.

Type narrowing with template guards

A structural directive in a template controls whether that template is rendered at run time. Some structural directives want to perform type narrowing based on the type of input expression.

There are two narrowings which are possible with input guards:

  • Narrowing the input expression based on a TypeScript type assertion function.
  • Narrowing the input expression based on its truthiness.

To narrow the input expression by defining a type assertion function:

      
// This directive only renders its template if the actor is a user.// You want to assert that within the template, the type of the `actor`// expression is narrowed to `User`.@Directive(...)class ActorIsUser {  @Input() actor: User|Robot;  static ngTemplateGuard_actor(dir: ActorIsUser, expr: User|Robot): expr is User {    // The return statement is unnecessary in practice, but included to    // prevent TypeScript errors.    return true;  }}

Type-checking will behave within the template as if the ngTemplateGuard_actor has been asserted on the expression bound to the input.

Some directives only render their templates when an input is truthy. It's not possible to capture the full semantics of truthiness in a type assertion function, so instead a literal type of 'binding' can be used to signal to the template type-checker that the binding expression itself should be used as the guard:

      
@Directive(...)class CustomIf {  @Input() condition!: any;  static ngTemplateGuard_condition: 'binding';}

The template type-checker will behave as if the expression bound to condition was asserted to be truthy within the template.

Typing the directive's context

If your structural directive provides a context to the instantiated template, you can properly type it inside the template by providing a static ngTemplateContextGuard type assertion function. This function can use the type of the directive to derive the type of the context, which is useful when the type of the directive is generic.

For the SelectDirective described above, you can implement an ngTemplateContextGuard to correctly specify the data type, even if the data source is generic.

      
// Declare an interface for the template context:export interface SelectTemplateContext<T> {  $implicit: T;}@Directive(...)export class SelectDirective<T> {  // The directive's generic type `T` will be inferred from the `DataSource` type  // passed to the input.  @Input({required: true}) selectFrom!: DataSource<T>;  // Narrow the type of the context using the generic type of the directive.  static ngTemplateContextGuard<T>(dir: SelectDirective<T>, ctx: any): ctx is SelectTemplateContext<T> {    // As before the guard body is not used at runtime, and included only to avoid    // TypeScript errors.    return true;  }}