Exploring ViewChild and ViewChildren in Angular

Angular offers a pair of powerful property decorators known as ViewChild and ViewChildren. These tools are essential for interacting with elements and child components within a component's template.

At their core, both decorators serve a similar purpose: they provide a way to gain a reference to, and subsequently manipulate, elements defined in the same template or to access child component instances.

Syntax breakdown
@ViewChild(selector, {read: readValue, static: staticValue})
propertyName

We'll delve into the specifics of this syntax as we progress through the examples.

To illustrate these concepts, let's build a counter component. This component will hold a counter value, starting at 0, and expose two methods to increment or decrement it by a specified amount, x. Our goal is to invoke these methods from a parent component.

First, we need to set up our environment. Let's create a component named my-counter and declare it as a child of the app-component. If you need a refresher on setting up parent-child relationships, this post provides a helpful guide.

After creating the component, we'll begin by adding the necessary code to its TypeScript file.

  counter = 0;
  constructor() { }
  ngOnInit(): void { }

  increaseCounter(x: number) {
    this.counter += x;
  }

  decreaseCounter(x: number) {
    this.counter -= x;
  }
Enter fullscreen mode Exit fullscreen mode

Understanding ViewChild and ViewChildren in Angular — figure 1
Next, we'll update the corresponding template file.

<p>Counter Value: {{ counter }}</p>
Enter fullscreen mode Exit fullscreen mode

Now, let's switch our attention to the app.component.ts file and add the following starter code.

  increaseCounter(x: number) { }

  decreaseCounter(x: number) { }
Enter fullscreen mode Exit fullscreen mode

The methods' implementations will be filled in shortly. And for the associated template:

<input (click)="increaseCounter(1)" type="button" 
value="Add by 1">

<input (click)="decreaseCounter(1)" type="button" 
value="Subtract by 1">
Enter fullscreen mode Exit fullscreen mode

Current output -
Understanding ViewChild and ViewChildren in Angular — figure 2
The button remains non-functional for now, but it will spring to life shortly.

Let's now get to the core of the matter.

First up: ViewChild.

We'll introduce a property called counterReference in app.component. This property is designed to hold a reference to the Counter component instance.

counterReference = {} as MyCounterComponent;
Enter fullscreen mode Exit fullscreen mode

This property must be adorned with the ViewChild decorator. The final implementation looks like this:

  @ViewChild(MyCounterComponent)
  counterReference = {} as MyCounterComponent;
Enter fullscreen mode Exit fullscreen mode

The ViewChild decorator accepts several parameters. The first, and most crucial, is the selector—in this case, the specific Component type you wish to obtain a reference to. Additionally, you can query using a template reference variable, a technique we'll explore shortly.

With the reference in hand, we can now implement the two previously empty methods.

  increaseCounter(x: number) {
    this.counterReference.increaseCounter(1);
  }

  decreaseCounter(x: number) {
    this.counterReference.decreaseCounter(1);
  }
Enter fullscreen mode Exit fullscreen mode

As demonstrated, with the counterReference property properly decorated, we can directly call the methods of the MyCounterComponent instance.

Clicking either button will now alter the counter's value.

Understanding ViewChild and ViewChildren in Angular — figure 3

This illustrates the fundamental concept: a parent component can leverage ViewChild to interact with its child's public API.

Now, let's examine an alternative approach using a template reference variable. Here’s a simple example of such a reference:

<div #myTemplateRef></div>
<app-my-counter #componentTemplateRef></app-my-counter>
Enter fullscreen mode Exit fullscreen mode

Notice the # symbol. This notation creates a variable that serves as a reference to an element or component within the template. In the snippet above, myTemplateRef and componentTemplateRef are the template reference variables.

To see this in action, let's update the component's TypeScript file with a few more lines.

  @ViewChild('myTemplateRef')
  myTemplateRef = {};

  @ViewChild('componentTemplateRef')
  componentTemplateRef = {};
Enter fullscreen mode Exit fullscreen mode

And in the corresponding template file:

<div #myTemplateRef></div>
<app-my-counter #componentTemplateRef></app-my-counter>
Enter fullscreen mode Exit fullscreen mode

Here's a critical piece of knowledge regarding the lifecycle of ViewChild.

When you generate a component using the CLI, you might have noticed the ngOnInit() method. This is one of Angular's lifecycle hook methods, and I'll cover all of them in detail in a future post. Crucially, there's another hook called ngAfterViewInit().

The ngAfterViewInit() method is invoked after the component's view (and its child views) have been fully initialized, meaning the view is ready. At this point, all properties decorated with ViewChild are available for use. Conversely, before this hook fires—for instance, within ngOnInit—these properties remain uninitialized or undefined.

To illustrate, here's what the properties look like inside ngOnInit:

Understanding ViewChild and ViewChildren in Angular — figure 4
And here's how they appear within ngAfterViewInit:

Understanding ViewChild and ViewChildren in Angular — figure 5
Here's a visual summary of the lifecycle:

Understanding ViewChild and ViewChildren in Angular — figure 6

The static option:
By default, the static flag is set to false.

Setting it to true enables support for creating embedded views at runtime. I'll revisit the static: true configuration when we discuss dynamic component creation in more depth.


Let's now shift our focus to ViewChildren. This decorator works much like ViewChild, but instead of a single reference, it returns all matching references as a QueryList.

A QueryList is an immutable collection that Angular maintains, automatically updating it whenever the application state changes and children are added, removed, or moved.

The QueryList offers several useful properties and methods: - first: Returns the first item in the list. - last: Returns the last item in the list. - length: Returns the number of items in the list. - changes: An observable that emits a new value whenever the list of children changes.

You can also use standard JavaScript array methods on a QueryList, such as map(), filter(), find(), and forEach().

Consider a scenario where you have three instances of the same component in your template.

<app-my-counter></app-my-counter>
<app-my-counter></app-my-counter>
<app-my-counter></app-my-counter>
Enter fullscreen mode Exit fullscreen mode

When you need to access and interact with all these instances, ViewChildren is the ideal solution.

  @ViewChildren(MyCounterComponent)
  viewChildrenRef: QueryList<MyCounterComponent> | undefined;
Enter fullscreen mode Exit fullscreen mode

By using ViewChildren, you can retrieve all instances matching your selector. This allows you to iterate over the collection and perform any necessary operations.

That concludes this exploration of these powerful Angular features.

I hope you found this information useful and engaging.

If you enjoyed this content, your support through likes ❤️, shares 💞, and comments 🧡 is always appreciated.

There are more Angular topics on the horizon, so stay tuned for future posts.

I'll be sharing more tips and tricks on Angular, JavaScript, TypeScript, and CSS over on Twitter.

I look forward to connecting with you there 😃

Cheers 🍻
Happy Coding