A template reference variable is often a reference to a DOM element within a template. It can also refer to a directive (which contains a component), an element, TemplateRef, or a web component.
Use the hash symbol (#) to declare a reference variable. The following reference variable, #phone
, declares a phone
variable on an <input>
element.
<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button
We can refer to a template reference variable anywhere in the component’s template. Here, a <button>
further down the template refers to the phone
variable.
Angular assigns each template reference variable a value based on where we declare the variable:
- If we declare the variable on a component, the variable refers to the component instance.
- If we declare the variable on a standard HTML tag, the variable refers to the element.
- If we declare the variable on an
<ng-template>
element, the variable refers to aTemplateRef
instance, which represents the template. - If the variable specifies a name on the right-hand side, such as
#var="ngModel"
, the variable refers to the directive or component on the element with a matchingexportAs
name
How a reference variable gets its value
In most cases, Angular sets the reference variable’s value to the element on which it is declared. In the previous example, phone
refers to the phone number <input>
. The button’s click handler passes the <input>
value to the component’s callPhone()
method.
The NgForm
directive can change that behavior and set the value to something else. In the following example, the template reference variable, itemForm
, appears three times separated by HTML.
<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
<label for="name"
>Name <input class="form-control" name="name" ngModel required />
</label>
<button type="submit">Submit</button>
</form>
<div [hidden]="!itemForm.form.valid">
<p>{{ submitMessage }}</p>
</div>
The reference value of itemForm
, without the ngForm
attribute value, would be the HTMLFormElement. There is, however, a difference between a Component
and a Directive
in that a Component
will be referenced without specifying the attribute value, and a Directive
will not change the implicit reference (that is, the element).
However, with NgForm
, itemForm
is a reference to the NgForm directive with the ability to track the value and validity of every control in the form.
The native <form>
element doesn’t have a form
property, but the NgForm
directive does, which allows disabling the submit button if the itemForm.form.valid
is invalid and passing the entire form control tree to the parent component’s onSubmit()
method.
The scope of a reference variable is the entire template. So, don’t define the same variable name more than once in the same template as the runtime value will be unpredictable.
Well! We cannot access the template reference variable inside the component class or typescript logic but still, we can use that variable by passing it through a method which will be called by the event listener.
<div>
<label for="Name">Name</label>
<input type="text" #nameInput>
<button (click)="onSaveName(nameInput)">Save Name</button>
</div>
Passing that local variable with omitting # symbol.
import {
Component,
} from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor() {}
onSaveName(name: HTMLInputElement) {
console.log(( < HTMLInputElement > name).value);
}
}
Here, we are expecting a name parameter which will be of type HTMLInputElement and also inside the body of the method, we have wrapped the name variable with the same type. It is because we are accessing the instance of the element which is <input> type.
We can see in the above screenshot that all the generic properties of Input element are wrapped under HTMLInputElement. It is one of good practice to explicitly define the parameter type we are about to receive in the method to avoid further confusion with variable types in the method body. If we do so, Editor intellisense will show us all the methods and properties can be called on the received parameter property.
@ViewChild()
There is one more way to accessing the template reference variable is using @ViewChild() decorator, using this decorator, we can reference that variable inside our component without passing it via method as a parameter, or we can say, if we need to access it before the onSaveName() method get executed.
import {
Component,
OnInit,
ViewChild,
ElementRef,
AfterViewChecked
} from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewChecked {
@ViewChild('nameInput') inputNameRef: ElementRef;
constructor() {}
ngOnInit() {}
onSaveName(name: HTMLInputElement) {
console.log(( < HTMLInputElement > name).value);
console.log(this.inputNameRef.nativeElement.value);
}
ngAfterViewChecked() {
console.log("After view got Checked");
console.log(this.inputNameRef.nativeElement.value);
}
}
Here, the output will be logged 3 times on the control, two for the method and one after AfterViewChecked lifecycle hook. Don’t forget that with the @ViewChild() decorator, our reference to the variable will be ElementRef type Since we are trying to access a reference to an element of our template view. ElementRef has sub-property called nativeElement which wraps all the underlying properties of that specific referenced element.
Alternative syntax
We can use the ref-
prefix alternative to #
. This example declares the fax
variable as ref-fax
instead of #fax
.
<input ref-fax placeholder="fax number" />
<button (click)="callFax(fax.value)">Fax</button>