Using @ViewChild in Angular
A ViewChild
is a reference to a component, directive or element within the template of the current component. We can use the @ViewChild()
decorator to access the child component from the parent component.
When to use the @ViewChild decorator?
Often you might see template references being used to access elements directly. For example, the #name
template reference might be used to get the value of the input field in the example below:
<input #name placeholder="Enter your name" />
<p>Hi {{name.value}}</p>
But sometimes you may need to access the entire <input />
component from within the controller. Order, you want better access to public methods on a custom component.
For example, take this template which contains a custom input field for currency:
<div>
<p>Enter an amount:</p>
<app-currency-input></app-currency-input>
<div>
<app-currency-input>
is a custom Angular component called CurrencyInputComponent
.
We can now use @ViewChild
to inject a reference to the AppCurrencyInputComponent
from within our controller like so:
...
export class AppComponent {
@ViewChild(CurrencyInputComponent) currencyInput: CurrencyInputComponent;
...
}
Is there a difference between the template reference and the ViewChild reference?
No. The injected CurrencyInputComponent
instance would be the same one had we use a template reference instead.
When is the ViewChild variable initialised?
The value of the ViewChild
variable is not immediately available when the component is constructed. Angular will automatically initialise the value when it's ready, but this will only be in the AfterViewInit
lifecycle hook.
That means you'll need to tread carefully if you're using the OnInit
lifecycle hook because there is a chance the value will be undefined
.
Here is an example of accessing a ViewChild
in the ngAfterViewInit
lifecycle hook:
...
@ViewChild(CurrencyInputComponent) currencyInput: CurrencyInputComponent;
ngAfterViewInit() {
console.log("currencyInput @ViewChild", this.currencyInput);
}
Depending on the context, there is a chance that the ViewChild
might already be initialised in the ngOnInit
lifecycle hook, but we should not count on it. It is always better to execute any logic which relies on the reference within ngAfterViewInit
to ensure your code is more robust and testable.
Accessing methods within a ViewChild component reference
Once you have access to the component via ViewChild
it's possible to access public properties and methods on that component. This feels strange initially because it begins to couple a parent component to a child component but it's also a great way to encapsulate related logic within a component and have the parent component orchestrate the execution.
Whilst there is nothing wrong with using a ViewChild
reference to execute methods within a child component, you'll need to be mindful of the component API you create. For example, marking class variables and methods as private
, protected
and public
will go a long way. Any methods marked as public will be accessible through the ViewChild
reference and therefore can be executed ad-hoc. Make sure your code is written in a way that can handle this scenario.
Take for example, our currency input component above which has a public method to convert the input to Canadian Dollars:
@Component({
selector: 'app-currency-input',
templateUrl: './currency-input.component.html'
})
export class CurrencyInputComponent {
...
public convert(currency: string) {
// logic to convert
}
}
Our parent component can create a ViewChild
reference and call the convert()
method.
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
@ViewChild(CurrencyInputComponent) currencyInput: CurrencyInputComponent;
...
handleButtonPress() {
this.currencyInput.convert("CAD");
}
}