Angular @ViewChild: In-Depth Explanation (All Features Covered)

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.

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 and div
  • 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:

Sample Application for trying out view child

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:

View Child base use case

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:

View Child cannot cross component boundaries

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:

Angular ViewChild example of injection of plain HTML elements

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:

Angular ViewChild example

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:

Angular ViewChild example

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:

Angular ViewChild example

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:

ViewChild sample application

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!

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:

Video Lessons Available on YouTube

Have a look at the Angular University Youtube channel, we publish about 25% to a third of our video tutorials there, new videos are published all the time.

Subscribe to get new video tutorials: