Angular ElementRef | Angular ViewChild & ViewChildren - Accessing Elements Correctly
Angular ElementRef | Angular ViewChild & ViewChildren - Accessing Elements Correctly

In this post you will learn what is ElementRef, ViewChild and ViewChildren inside Angular.

Here I already prepared for us a small component. I created a child component with several methods.

export class ChildComponent {
  count = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

Here is our child template

{{count}}

This is just a small component with counter and 2 methods. And inside our parent we render our child component.

<child></child>

In browser we just rendered zero and nothing more.

Initial project

View child

What we want to do now is increase our child's counter from the parent. In order to do that I want to create a button.

<button (click)="increment()">Increment child</button>
<child></child>

How we can access a child component and run it's function when we click on the button?

export class AppComponent {
  @ViewChild(ChildComponent, {static: true}) child?: ChildComponent;

  increment() {
    this.child?.increment();
  }
}

Here we used ViewChild where we pass our ChildComponent in order to get access of the component that we rendered in markup. Static true means that we are sure that this component is always there and it is not wrapped in some if condition.

This allows us to call this.child?.increment() inside to change the counter.

Increased counter

As you can see now we can increase our counter from the parent component.

ViewChild allows us to get access to the child component and do whatever we want with it.

But I must tell you that it is not the best approach. The default way to do something like this is Angular is by using Inputs and Outputs. Which actually means your child must have an Output increment if you want to allow changing it from outside. We should not brutforce this increment method through ViewChild if we can avoid it.

We can also use ViewChild not only with components but also with DOM elements.

<button (click)="increment()" #button>Increment child</button>
<child></child>

Here I added #button on our HTML element which allows me to get access to it in the component.

export class AppComponent implements AfterViewInit {
  @ViewChild('button', {static: true}) buttonRef?: ElementRef<HTMLButtonElement>;

  ngAfterViewInit() {
    if (this.buttonRef?.nativeElement) {
      this.buttonRef.nativeElement.innerHTML = 'fooo'
    }
  }
}

Here we used ViewChild to get an HTML element. We wrote code related to it in ngAfterViewInit because we want to be sure that our DOM element is already rendered. Then we changed the text of the button to fooo.

ViewChild button

As you can see in browser we successfully changed the text of the button through DOM access.

View children

If you have several elements on the screen it makes a lot of sense to use ViewChildren in order to get access to them as an array. But in order to test that let's add several components inside.

<child></child>
<child></child>
<child></child>
export class AppComponent {
  @ViewChildren(ChildComponent, {static: true}) children?: QueryList<ChildComponent>;

  ngAfterViewInit() {
    this.children?.forEach(child => console.log(child))
  }
}

The code is super similar but we get back a QueryList which is an array of DOM elements.

View children

As you can see we got access to the list of our children components.

In the same way we can get access to the list of DOM elements.

export class AppComponent implements AfterViewInit {
  @ViewChildren('button', {static: true}) buttonsRef?: ElementRef<HTMLButtonElement>;

  ngAfterViewInit() {
    this.buttonsRef?.forEach(button => console.log(button))
  }
}

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.