liveBook · Manning

Template variable

As explained in Pro Angular 16

A template reference variable is an Angular template variable that refers to a template element or directive instance.

A template variable gives an Angular template a name it can use in bindings. There are two useful forms: `let` receives data supplied by a structural directive, while `#` creates a reference to a template element or an exported directive instance. Use a variable when a binding needs the current item, an element’s value, or a directive’s state without adding a property to the component. section 12.2.3 section 13.2.3 section 14.7

Use data from a repeated row

`<tr *ngFor="let item of products(); let i = index; let c = count">` makes `item` the current product, `i` its zero-based position, and `c` the number of products. Cells inside the row can display `{{item.name}}` and `{{i + 1}} of {{c}}`. The directive supplies these values for each generated row; `odd`, `even`, `first`, and `last` can likewise drive class or conditional bindings. section 12.2.3

A reference variable starts with `#`. On a plain input, `<input #product>` assigns `product` the input’s `HTMLInputElement`, so `{{product.value || '(None)'}}` reads its current value. Other bindings in the template can set that value. This is useful for direct element access, but it is not a substitute for ordinary component-backed data binding: in the input example, an `(input)="false"` event binding is needed to trigger updates when the user types. section 13.2.3

Read validation state instead of the DOM element

`<input name="name" [(ngModel)]="newProduct.name" #name="ngModel" required />` assigns `name` the value exported by `ngModel`, not the input element. A message list can use `*ngIf="name.dirty && name.invalid"`, then inspect `name.errors?.['required']`. For a whole form, `<form #form="ngForm" (ngSubmit)="submitForm(form)">` passes its `NgForm` instance to the component, where `form.valid` can be checked before accepting data and `form.resetForm()` can prepare the form for reuse. section 13.4.2 section 13.4.3

A custom attribute directive can offer the same pattern. Set `exportAs: "paModel"` in its `@Directive` decorator, then write `#model="paModel"` on the host element and use a property such as `{{model.direction}}` in a binding. The variable name need not match the export name; choosing a clear name makes the source of the property easier to recognize. Exporting exposes the directive’s methods and properties to template expressions, so avoid treating internal members as a template API. section 14.7

Structural directives provide a different route to named data: an explicit `<ng-template [paForOf]="products()" let-item let-i="index" let-odd="odd">` receives properties from the directive’s context object. Here `let-item` receives its `$implicit` value, while `let-i` and `let-odd` name context properties. A reference variable can also name an `<ng-template>` for `ngTemplateOutlet`; a `let-` variable inside that template receives values supplied through `ngTemplateOutletContext`. section 15.3.1 section 12.2.4

Listing 13.9. Using a template variable in the template.html file in the src/app folder
<div class="p-2">
    <div class="bg-info text-white p-2">
        Selected Product: {{product.value || '(None)'}}
    </div>    
    <table class="table table-sm table-bordered">
        <tr *ngFor="let item of products(); let i = index"
              [class.bg-info]="product.value == item.name">
            <td (mouseover)="product.value = item.name ?? ''">
                {{i + 1}}
            </td>
            <td>{{item.name}}</td>
            <td>{{item.category}}</td>
            <td>{{item.price}}</td>
        </tr>
    </table>
    <div class="form-group">
        <label>Product Name</label>
        <input #product class="form-control" (input)="false" />
    </div> 
</div>

The product table shows a `let` row variable and a `#` input reference working in the same template, including the event binding needed to refresh the displayed input value. section 13.2.3

Listing 13.17. Adding validation messages in the template.html file in the src/app folder
<div class="p-2">
    <div class="bg-info text-white mb-2 p-2">
        Model Data: {{jsonProduct}}
    </div>
 
    <form (ngSubmit)="addProduct(newProduct)">
        <div class="form-group">
            <label>Name</label>
            <input class="form-control"
                name="name"
                [(ngModel)]="newProduct.name"
                #name="ngModel"
                required
                minlength="5"
                pattern="^[A-Za-z ]+$" />
            <ul class="text-danger list-unstyled mt-1" 
                    *ngIf="name.dirty && name.invalid">
                <li *ngIf="name.errors?.['required']">
                    You must enter a product name
                </li>
                <li *ngIf="name.errors?.['pattern']">
                    Product names can only contain letters and spaces
                </li>
                <li *ngIf="name.errors?.['minlength']">
                    Product names must be at least
                    {{ name.errors?.['minlength'].requiredLength }} 
                    characters
                </li>
            </ul>        
        </div>
        <button class="btn btn-primary mt-2" type="submit">
            Create
        </button>
    </form>
</div>

The form listing shows `#name="ngModel"` alongside bindings that display messages only when the field is dirty and invalid. section 13.4.2

How Pro Angular 16 covers it

How deep it goes
Works through `let` context values, `#` element references, validation exports, and custom `exportAs`; defers fuller directive-export mechanics until custom directives.
Examples
Uses product-table rows, an input’s value, field and form validation messages, a custom directive’s direction, and repeated template content.
What it assumes
Assumes familiarity with Angular bindings and templates while introducing `let` and `#` variable syntax through examples.
What you can do after
Apply it in Angular templates to read element values, render directive context data, and access exported validation or custom-directive state.
How it connects
Connects `ngFor`, structural directive contexts, `ngModel`, `ngForm`, `ngTemplateOutlet`, and attribute directive exports.

Related concepts

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