The Angular @ViewChild decorator is one of the first decorators that you will run into while learning Angular, as it's also one of the most commonly used decorators.
This decorator has a lot of features: some of them might not be very well known but they are extremely useful.
In this post, we are going to quickly cover all the features that we have available in this decorator while giving practical examples for each use case along the way.
Notice that this decorator has a newer alternative which is signal-based: the
viewChild()
signal query. You can read all about it here:
Angular Signal Queries: viewChild, contentChild, viewChildren, contentChildren (Complete Guide)
Table of Contents:
In this post, we will cover the following topics:
- When do we need the @ViewChild decorator?
- The AfterViewInit Lifecycle Hook
- What is the scope of @ViewChild template queries?
- using @ViewChild to inject a component
- how to use @ViewChild to inject a plain HTML element
- using @ViewChild to inject the plain HTML element of a component
- how to use @ViewChild to inject one of the multiple directives applied to a single element or component
- Code Samples (GitHub Repository)
- Conclusion
So without further ado, let's get started with our @ViewChild decorator deep dive!
When do we need the @ViewChild decorator?
As we know, in Angular we define a component template by combining plain HTML elements with other Angular components.
As an example, we have here an Angular AppComponent
template that mixes both HTML and custom components in its template:
As we can see, this template includes several different elements types, such as:
- plain HTML elements like
h2
anddiv
- custom application components like the
<color-sample>
component - third-party components (like a color picker)
- as well as multiple Angular Material components
And this is what the AppComponent
looks like on screen:
We are going to base all our examples in this initial template. The <color-sample>
component is the little palette blue square, and next to it we have an input that is linked to a color picker popup.
When to use the @ViewChild decorator?
Many times we can coordinate these multiple components and HTML elements directly in the template by using template references like #primaryInput
or #primaryColorSample
, without using the AppComponent
class.
But this is not always the case! Sometimes, the AppComponent
might need references to the multiple elements that it contains inside its template, in order to mediate their interaction.
If that's the case, then we can obtain references to those template elements and have them injected into the AppComponent
class by querying the template: that's what @ViewChild is for.
Using @ViewChild to inject a reference to a component
Let's say that AppComponent
needs a reference to the <color-sample>
component that it uses inside its template, in order to call a method directly on it.
In that case, we can inject a reference to the <color-sample>
instance named #primaryColorSample
by using @ViewChild:
By using @ViewChild, the primarySampleComponent
member variable is going to be filled in by Angular with a ColorSampleComponent
instance.
This injected ColorSampleComponent
instance is the same one linked to the <color-sample>
custom element present in the template.
When are variables injected via @ViewChild available?
The value of this injected member variable is not immediately available at component construction time!
Angular will fill in this property automatically, but only later in the component lifecycle, after the view initialization is completed.
The AfterViewInit Lifecycle Hook
If we want to write component initialization code that uses the references injected by @ViewChild, we need to do it inside the AfterViewInit
lifecycle hook.
Here is an example of how to use this lifecycle hook:
If we now run this program, here is the output that we get in the console:
As we can see, Angular has filled automatically our member variable primaryColorSample
with a reference to a component!
Can we use ngOnInit() instead of ngAfterViewInit()?
If we want to make sure that the references injected by @ViewChild are present, we should always write our initialization code using ngAfterViewInit()
.
Depending on the situation, the template references might already be present on ngOnInit()
, but we shouldn't count on it.
What is the scope of the @ViewChild template queries?
With @ViewChild, we can inject any component or directive (or HTML element) present on the template of a given component onto the component itself.
But how far can we query components down the component tree? Let's try to use @ViewChild to query a component that is deeper in the component tree.
As an example, let's have a look at the <color-sample>
component:
As we can see, this component internally uses the <mat-icon>
component, to display the small palette icon.
Let's now go ahead and see if we can query that <mat-icon>
component and inject it directly into AppComponent:
If we try to run this, this is what we get in the console:
As we can see in the console results:
The @ViewChild decorator cannot see across component boundaries!
Visibility scope of @ViewChild template queries
This means that queries done using @ViewChild can only see elements inside the template of the component itself. It's important to realize that @ViewChild cannot be used to inject:
- Anything inside the templates of its child components
- and neither anything in the template of parent components as well
To summarize: the @ViewChild decorator is a template querying mechanism that is local to the component.
With this, we have covered the most common use case of @ViewChild, but there is still a lot more to it: let's see some more use cases!
Using @ViewChild to inject a reference to a DOM element
Instead of injecting a direct child component, we might want to interact directly with a plain HTML element of the template, such as for example the h2
title tag inside AppComponent
.
In order to do that, we need to first assign a template reference to the HTML tag that we want to inject:
As we can see, we have assigned the #title
template reference to the h2
tag. We can now have the h2
element injected directly into our component class in the following way:
As we can see, we are passing the string 'title'
to the @ViewChild decorator, which corresponds to the name of the template reference applied to the h2
tag.
Because h2
is a plain HTML element and not an Angular component, what we get injected this time is a wrapped reference to the native DOM element of the h2
tag:
ElementRef
simply wraps the native DOM element, and we can retrieve it by accessing the nativeElement
property.
Using the nativeElement
property, we can now apply any native DOM operation to the h2
title tag, such as for example addEventListener()
.
And this is how we can use @ViewChild to interact with plain HTML elements in the template, but this leads us to the question:
What to do if we need the DOM element that is associated with an Angular component instead?
After all, the <color-sample>
HTML tag is still a DOM element, even though it has an instance of ColorSampleComponent
attached to it.
Using @ViewChild to inject a reference to the DOM element of a component
Let's give an example for this new use case. Take for example the <color-sample>
component inside AppComponent
:
The <color-sample>
component has a template reference #primaryColorSample
assigned to it.
Let's see what happens if we now try to use this template reference to inject the <color-sample>
DOM element like we did with the h2
tag:
If we run this program, we might be surprised to find out that this time we are not getting back the native DOM element:
Default behaviour of @ViewChild injection by template reference
Instead, we are getting back again the ColorSampleComponent
instance! And this is indeed the default behavior of @ViewChild when using injection by template reference name:
-
when injecting a reference applied to a component, we get back the component instance
-
when injecting a reference to a plain HTML element, we get back the corresponding wrapped DOM element
The @ViewChild options argument
But in the case of our <color-sample>
component, we would like to get the DOM element that is linked to the component! This is still possible, by using the second argument of the @ViewChild decorator:
As we can see, we are passing a second argument containing a configuration object with the read
property set to ElementRef
.
This read
property will specify exactly what we are trying to inject, in case that there are multiple possible injectables available.
In this case, we are using the read
property to specify that we want to get the DOM element (wrapped by ElementRef
) of the matched template reference, and not the component.
If we now run our program, this is indeed what we get in the console:
Let's now see another common use case when the @ViewChild read
property might come in handy.
Using @ViewChild to inject a reference to one of several directives
As our application uses more and more directives and libraries, we will likely need the read
property more and more.
For example, going back to our color picker example, let's now try to do something simple like opening the color picker when the color sample gets clicked:
In this example, we are trying to integrate our components by using template
references only.
We are detecting the click in <color-sample>
, and when that occurs we are trying to use the reference #primaryInput
to access the colorPicker
directive, and open the dialog.
Using template references is a good approach that will work in many cases, but not here!
In this case, the template reference #primaryInput
points to the DOM element <input>
, and not to the colorPicker
directive applied to that same element.
If we run this version of the program, we will get the following error:
This error occurs because, by default, the template reference primaryInput
points to the input box DOM element, and not to the colorPicker
directive.
Using @ViewChild to inject Directives
As we can see, this is not the way to get a reference to a directive, especially in a situation when multiple directives are applied to the same plain HTML element or Angular component.
In order to solve this, we are going to first rewrite our template so that the handling of the click event is now delegated to the AppComponent
class:
Then in the AppComponent
class, we are going to have the color picker directive injected in the following way:
And with this, we now have a correct reference to the colorPicker
directive!
If we now click on the small palette icon, the color picker will now get opened as expected:
And with this last example, we have now covered all the features of the @ViewChild operator, and some of their intended use cases. Let's now summarize what we have learned.
Code Samples (GitHub Repository)
All the running code for these examples can be found in the following GitHub repository.
Conclusion
The @ViewChild decorator allows us to inject into a component class references to elements used inside its template, that's what we should use it for.
Using @ViewChild we can easily inject components, directives or plain DOM elements. We can even override the defaults of @ViewChild and specify exactly we need to inject, in case that multiple options are available.
@ViewChild is a local component template querying mechanism, that cannot see the internals of its child components.
By injecting references directly into our component class, we can easily write any coordination logic that involves multiple elements of the template.
Why we don't always need @ViewChild
Let's also remember that there are many use cases when this decorator might not actually be needed, because many simple interactions can be coded directly in the template using template references, without the need to use the component class.
I hope that this post helps with better understanding @ViewChild, and that you enjoyed it!
And if you would like to know about more advanced Angular Core features like @ViewChild
, we recommend checking the Angular Core Deep Dive course, where we cover Angular core in much more detail.
If you have some questions or comments please let me know in the comments below and I will get back to you.
To get notified of upcoming posts on Angular Universal and other Angular topics, I invite you to subscribe to our newsletter:
If you are just getting started learning Angular, have a look at the Angular for Beginners Course: