[html] How to declare a variable in a template in Angular

I have the following template :

<div>
  <span>{{aVariable}}</span>
</div>

and would like to end up with :

<div "let a = aVariable">
  <span>{{a}}</span>
</div>

Is there a way to do it ?

This question is related to html angular

The answer is


For those who decided to use a structural directive as a replacement of *ngIf, keep in mind that the directive context isn't type checked by default. To create a type safe directive ngTemplateContextGuard property should be added, see Typing the directive's context. For example:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
    // don't use 'ng' prefix since it's reserved for Angular
    selector: '[appVar]',
})
export class VarDirective<T = unknown> {
    // https://angular.io/guide/structural-directives#typing-the-directives-context
    static ngTemplateContextGuard<T>(dir: VarDirective<T>, ctx: any): ctx is Context<T> {
        return true;
    }

    private context?: Context<T>;

    constructor(
        private vcRef: ViewContainerRef,
        private templateRef: TemplateRef<Context<T>>
    ) {}

    @Input()
    set appVar(value: T) {
        if (this.context) {
            this.context.appVar = value;
        } else {
            this.context = { appVar: value };
            this.vcRef.createEmbeddedView(this.templateRef, this.context);
        }
    }
}

interface Context<T> {
    appVar: T;
}

The directive can be used just like *ngIf, except that it can store false values:

<ng-container *appVar="false as value">{{value}}</ng-container>

<!-- error: User doesn't have `nam` property-->
<ng-container *appVar="user as user">{{user.nam}}</ng-container>

<ng-container *appVar="user$ | async as user">{{user.name}}</ng-container>

The only drawback compared to *ngIf is that Angular Language Service cannot figure out the variable type so there is no code completion in templates. I hope it will be fixed soon.


Short answer which help to someone

  • Template Reference variable often reference to DOM element within a template.
  • Also reference to angular or web component and directive.
  • That means you can easily access the varible anywhere in a template

enter image description here

enter image description here

  • Declare reference variable using hash symbol(#)
  • Can able to pass a variable as a parameter on an event

enter image description here

  show(lastName: HTMLInputElement){
    this.fullName = this.nameInputRef.nativeElement.value + ' ' + lastName.value;
    this.ctx.fullName = this.fullName;
  }

*However, you can use ViewChild decorator to reference it inside your component.

import {ViewChild, ElementRef} from '@angular/core';

Reference firstNameInput variable inside Component

@ViewChild('firstNameInput') nameInputRef: ElementRef;

After that, you can use this.nameInputRef anywhere inside your Component.

Working with ng-template

In the case of ng-template, it is a little bit different because each template has its own set of input variables.

enter image description here

https://stackblitz.com/edit/angular-2-template-reference-variable


original answer by @yurzui won't work startring from Angular 9 due to - strange problem migrating angular 8 app to 9. However, you can still benefit from ngVar directive by having it and using it like

<ng-template [ngVar]="variable">
your code
</ng-template>

although it could result in IDE warning: "variable is not defined"


Here is a directive I wrote that expands on the use of the exportAs decorator parameter, and allows you to use a dictionary as a local variable.

import { Directive, Input } from "@angular/core";
@Directive({
    selector:"[localVariables]",
    exportAs:"localVariables"
})
export class LocalVariables {
    @Input("localVariables") set localVariables( struct: any ) {
        if ( typeof struct === "object" ) {
            for( var variableName in struct ) {
                this[variableName] = struct[variableName];
            }
        }
    }
    constructor( ) {
    }
}

You can use it as follows in a template:

<div #local="localVariables" [localVariables]="{a: 1, b: 2, c: 3+2}">
   <span>a = {{local.a}}</span>
   <span>b = {{local.b}}</span>
   <span>c = {{local.c}}</span>
</div>

Of course #local can be any valid local variable name.


I liked the approach of creating a directive to do this (good call @yurzui).

I ended up finding a Medium article Angular "let" Directive which explains this problem nicely and proposes a custom let directive which ended up working great for my use case with minimal code changes.

Here's the gist (at the time of posting) with my modifications:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'

interface LetContext <T> {
  appLet: T | null
}

@Directive({
  selector: '[appLet]',
})
export class LetDirective <T> {
  private _context: LetContext <T> = { appLet: null }

  constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef <LetContext <T> >) {
    _viewContainer.createEmbeddedView(_templateRef, this._context)
  }

  @Input()
  set appLet(value: T) {
    this._context.appLet = value
  }
}

My main changes were:

  • changing the prefix from 'ng' to 'app' (you should use whatever your app's custom prefix is)
  • changing appLet: T to appLet: T | null

Not sure why the Angular team hasn't just made an official ngLet directive but whatevs.

Original source code credit goes to @AustinMatherne


It is much simpler, no need for anything additional. In my example I declare variable "open" and then use it.

   <mat-accordion class="accord-align" #open>
      <mat-expansion-panel hideToggle="true" (opened)="open.value=true" (closed)="open.value=false">
        <mat-expansion-panel-header>
          <span class="accord-title">Review Policy Summary</span>
          <span class="spacer"></span>
          <a *ngIf="!open.value" class="f-accent">SHOW</a>
          <a *ngIf="open.value" class="f-accent">HIDE</a>
        </mat-expansion-panel-header>
        <mat-divider></mat-divider>
        <!-- Quote Details Component -->
        <quote-details [quote]="quote"></quote-details>
      </mat-expansion-panel>
    </mat-accordion>

You can declare variables in html code by using a template element in Angular 2 or ng-template in Angular 4+.

Templates have a context object whose properties can be assigned to variables using let binding syntax. Note that you must specify an outlet for the template, but it can be a reference to itself.

<ng-template let-a="aVariable" [ngTemplateOutletContext]="{ aVariable: 123 }" [ngTemplateOutlet]="selfie" #selfie>
  <div>
    <span>{{a}}</span>
  </div>
</ng-template>

<!-- Output
<div>
  <span>123</span>
</div>
-->

You can reduce the amount of code by using the $implicit property of the context object instead of a custom property.

<ng-template let-a [ngTemplateOutletContext]="{ $implicit: 123 }" [ngTemplateOutlet]="t" #t>
  <div>
    <span>{{a}}</span>
  </div>
</ng-template>

The context object can be a literal object or any other binding expression. Even pipes seem to work when surrounded by parentheses.

Valid examples of ngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" use with let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" use with let-a
  • [ngTemplateOutletContext]="ctx" where ctx is a public property

I would suggest this: https://medium.com/@AustinMatherne/angular-let-directive-a168d4248138

This directive allow you to write something like:

<div *ngLet="'myVal' as myVar">
  <span> {{ myVar }} </span>
</div>

Ugly, but:

<div *ngFor="let a of [aVariable]">
  <span>{{a}}</span>
</div>

When used with async pipe:

<div *ngFor="let a of [aVariable | async]">
  <span>{{a.prop1}}</span>
  <span>{{a.prop2}}</span>
</div>

If you need autocomplete support from within in your templates from the Angular Language Service:

Synchronous:

myVar = { hello: '' };

<ng-container *ngIf="myVar; let var;">
  {{var.hello}}
</ng-container>

Using async pipe:

myVar$ = of({ hello: '' });

<ng-container *ngIf="myVar$ | async; let var;">
  {{var.hello}}
</ng-container>

update 3

Issue 2451 is fixed in Angular 4.0.0

See also

update 2

This isn't supported.

There are template variables but it's not supported to assign arbitrary values. They can only be used to refer to the elements they are applied to, exported names of directives or components and scope variables for structural directives like ngFor,

See also https://github.com/angular/angular/issues/2451

Update 1

@Directive({
  selector: '[var]',
  exportAs: 'var'
})
class VarDirective {
  @Input() var:any;
}

and initialize it like

<div #aVariable="var" var="abc"></div>

or

<div #aVariable="var" [var]="'abc'"></div>

and use the variable like

<div>{{aVariable.var}}</div>

(not tested)

  • #aVariable creates a reference to the VarDirective (exportAs: 'var')
  • var="abc" instantiates the VarDirective and passes the string value "abc" to it's value input.
  • aVariable.var reads the value assigned to the var directives var input.

In case if you want to get the response of a function and set it into a variable, you can use it like the following in the template, using ng-container to avoid modifying the template.

<ng-container *ngIf="methodName(parameters) as respObject">
  {{respObject.name}}
</ng-container>

And the method in the component can be something like

methodName(parameters: any): any {
  return {name: 'Test name'};
}

I am using angular 6x and I've ended up by using below snippet. I've a scenerio where I've to find user from a task object. it contains array of users but I've to pick assigned user.

<ng-container *ngTemplateOutlet="memberTemplate; context:{o: getAssignee(task) }">
</ng-container>
<ng-template #memberTemplate let-user="o">
  <ng-container *ngIf="user">
    <div class="d-flex flex-row-reverse">
      <span class="image-block">
        <ngx-avatar placement="left" ngbTooltip="{{user.firstName}} {{user.lastName}}" class="task-assigned" value="28%" [src]="user.googleId" size="32"></ngx-avatar>
      </span>
    </div>
  </ng-container>
</ng-template>