ViewChild no Angular: Aprenda a acessar elementos filhos com facilidade

Aprenda a interagir com elementos filhos no Angular usando ViewChild. Ele permite manipular elementos do DOM e componentes filhos.

Banner de divulgação sobre o tema ViewChild no framework Angular
ViewChild no Angular: Aprenda a acessar elementos filhos com facilidade

O uso do ViewChild no Angular permite uma interação programática com os elementos da tela, permitindo a manipulação de propriedades, eventos e métodos desses elementos dentro do componente Angular. Essa funcionalidade é particularmente útil para realizar diversas tarefas, tais como acesso ao DOM (Document Object Model), modificação dinâmica de elementos HTML e integração com bibliotecas externas.

Neste artigo, discutiremos detalhadamente o seu uso e forneceremos um exemplo prático para ilustrar seu funcionamento em um cenário real. Além disso, abordaremos a última novidade da versão 17.2: o viewChild (não ViewChild), uma variação que utiliza o signal. Vale ressaltar que esta funcionalidade ainda está em developer preview.

ℹ️
Utilizei a versão 17.2 do Angular para construir os exemplos ao longo texto.

Uso básico do ViewChild

Quando utilizamos o decorador @ViewChild("el"), o Angular realizará uma busca dentro do HTML para identificar o primeiro elemento marcado com #el. Veja no exemplo a seguir:

import { Component, ElementRef, ViewChild } from "@angular/core";

@Component({
  selector: "app-exemplo1",
  standalone: true,
  template: `
    <div #el>Olá sou o exemplo 1</div>
    <button (click)="testar()">Testar</button>
  `,
})
export class Exemplo1Component {
  @ViewChild("el") divElement!: ElementRef<HTMLDivElement>;

  testar() {
    console.log(`Div encontrado: ${!!this.divElement}`);
  }
}
Navegador mostrando o projeto em execução. Ao clicar no botão "Testar" aparece uma mensagem no console do DevTools indicando que o div#el foi localizado
Utilizando o @ViewChild para pegar a referência do <div #el>

É um processo bem simples e dinâmico, tanto que caso o elemento procurado não esteja na tela em um dado momento, o Angular atualiza o valor de forma automática, ou seja, a pesquisa é dinâmica:

import { Component, ElementRef, ViewChild } from "@angular/core";

@Component({
  selector: "app-exemplo2",
  standalone: true,
  template: `
    @if (mostrarDiv) {
    <div #el>Olá sou o exemplo 1</div>
    }

    <button (click)="testar()">Testar</button>
    <button (click)="mostrarDiv = !mostrarDiv">Mostrar/Esconder Div</button>
  `,
})
export class Exemplo2Component {
  mostrarDiv = true;

  @ViewChild("el") divElement!: ElementRef<HTMLDivElement>;

  testar() {
    console.log(`Div encontrado: ${!!this.divElement}`);
  }
}
Navegador mostrando o projeto em execução mostrando que o `ViewChild` pesquisa o elemento na tela dinamicamente
Exemplo mostrando que a pesquisa do ViewChild é dinâmica

Aplicando o foco em um input

Um exemplo bastante comum em que costumo utilizar o @ViewChild é no processo de reset de formulários. Quando limpamos um formulário, ou seja, realizamos o "reset", é comum direcionar o foco para o primeiro elemento do formulário para facilitar a vida do usuário. Para isso, utilizo o @ViewChild de maneira semelhante ao comando tradicional document.getElementById('id-do-input').focus(). Veja o exemplo abaixo para entender melhor:

import {
 Component,
 ElementRef,
 ViewChild
} from '@angular/core';
import {
 FormControl,
 FormGroup,
 ReactiveFormsModule,
 Validators
} from '@angular/forms';

@Component({
 selector: 'app-exemplo3',
 standalone: true,
 imports: [ReactiveFormsModule],
 template: `
  <form [formGroup]="form" (ngSubmit)="salvar()">
   <div>
    <input
     #nome
     autofocus
     formControlName="nome"
     placeholder="nome..."
     type="text"
    />
   </div>
   <div>
    <input
     formControlName="cargo"
     placeholder="cargo..."
     type="text"
    />
   </div>
   <div>
    <input type="submit" value="Salvar" />
   </div>
  </form>
 `
})
export class Exemplo3Component {
 form = new FormGroup({
  nome: new FormControl('', {
   validators: [Validators.required]
  }),
  cargo: new FormControl('', {
   validators: [Validators.required]
  })
 });

 @ViewChild('nome') elNome!: ElementRef<HTMLInputElement>;

 reset() {
  this.form.reset();
  this.elNome.nativeElement.focus();
 }

 salvar() {
  if (this.form.invalid) {
   return;
  }

  console.log('Valores do formulário', this.form.value);
  this.reset();
 }
}
Navegador mostrando o projeto em execução. Ao efetuar o submit o primeiro input recebe o foco
Utilizando o @ViewChild para pegar a referência de um input e acionar o .focus()

Pegando a referência de um componente

Também é possível passar o nome da classe do componente que estamos pesquisando no template:

@ViewChild(NomeDoComponente) variavel: NomeDoComponente;

Essa abordagem é especialmente útil quando precisamos acessar recursos específicos do componente em si. No exemplo a seguir, o Exemplo4Component utiliza o componente <app-ola> em seu template. Utilizando o @ViewChild podemos pegar a referência do componente e acessar os recursos do objeto, como por exemplo, chamar o método alterarTexto da classe OlaComponent (app-ola):

import { Component } from '@angular/core';

@Component({
 selector: 'app-ola',
 standalone: true,
 template: `<p>{{ texto }}</p>`
})
export class OlaComponent {
 texto = 'olá!';

 alterarTexto() {
  this.texto = 'olá mundo!';
 }
}
import { Component, ViewChild } from '@angular/core';
import { OlaComponent } from '../ola/ola.component';

@Component({
 selector: 'app-exemplo4',
 standalone: true,
 imports: [OlaComponent],
 template: `
  <app-ola></app-ola>
  <button (click)="testar()">testar</button>
 `
})
export class Exemplo4Component {
 @ViewChild(OlaComponent) olaComponent!: OlaComponent;

 testar() {
  // Acessando o método de um outro componente
  this.olaComponent.alterarTexto();
 }
}
Navegador mostrando o projeto em execução. Ao clicar no botão "Testar" o texto na tela é alterado de "olá!" para "olá mundo!"
Utilizando o @ViewChild para pegar a referência de um componente

Utilizando o novo viewChild

O lançamento da versão v17.2.0 marca outro passo significativo na jornada dos signals dentro do Angular com a introdução dos Queries Signals. Esta funcionalidade, ainda em developer preview, traz um conjunto de novas funções para realizar consultas de elementos: viewChild, viewChildren, contentChild e contentChildren.

A diferença é que, ao acessarmos um elemento filho, seu acesso é feito por meio de um Signal, um tema previamente abordado aqui:

Introdução ao Angular Signals
Ainda em developer preview, Angular Signals é um novo recurso que pode mudar o atual sistema de detecção de mudanças do Angular. Criando “pacote” ao redor de um valor e pode notificar consumidores interessados nas mudanças do valor. Conheça os motivadores e veja alguns exemplos neste post

Veja no exemplo abaixo:

import {
 Component,
 ElementRef,
 effect,
 viewChild
} from '@angular/core';
import { OlaComponent } from '../ola/ola.component';

@Component({
 selector: 'app-exemplo5',
 standalone: true,
 imports: [OlaComponent],
 template: `
  @if (mostrarDiv) {
  <div #el>Olá sou o exemplo 5</div>
  <app-ola></app-ola>
  }
  <button (click)="testar()">Testar</button>
  <button (click)="mostrarDiv = !mostrarDiv">
   Mostrar/Esconder Div
  </button>
 `
})
export class Exemplo5Component {
 mostrarDiv = true;

 divElement = viewChild<ElementRef<HTMLDivElement>>('el');
 olaComponent = viewChild(OlaComponent);

 constructor() {
  // Como os elementos são acessados através do Signal,
  // podemos por exemplo usar o effect.
  // Caso tenha dúvidas sobre o effect ou Signal:
  // https://consolelog.com.br/introducao-angular-signals
  effect(() => {
   console.log('div#el encontrado:', !!this.divElement());
   console.log(
    'olaComponent encontrado:',
    !!this.olaComponent()
   );
  });
 }

 testar() {
  console.log(`Div encontrado: ${!!this.divElement}`);

  // Quando temos a certeza de que vamos encontrar o
  // <app-ola>, podemos utilizar o required:
  //
  //   olaComponent = viewChild.required(OlaComponent);
  //
  // Assim não precisamos do ?. (optional chaining operator)
  this.olaComponent()?.alterarTexto();
 }
}
Navegador mostrando o projeto em execução.
Exemplo utilizando o novo viewChild

Considerações

Além dos exemplos que citei neste texto, no passado já havia usado este recurso durante a implementação do Google ReCaptcha. Vou deixar o link abaixo:

Como usar o reCAPTCHA - Angular & ExpressJS
Como adicionar o Google reCATPCHA na tela de login de um projeto Angular e uma API com Express JS + Node.js.

Caso já tenha entendi bem o funcionamento do ViewChild, sugiro estudar ViewChildren, ContentChild e ContentChildren.

Abaixo há alguns links interessantes sobre o ViewChild:

Versões do Angular (fev/2024)

Version   Status  Released    Active ends  LTS ends   
=======   ======  ==========  ==========   ==========
^17.0.0   Active  2023-11-08  2024-05-08   2025-05-15 
^16.0.0   LTS     2023-05-03  2023-11-08   2024-11-08 
^15.0.0   LTS     2022-11-18  2023-05-03   2024-05-18 

Fonte: https://angular.io/guide/releases#actively-supported-versions
ANGULAR             NODEJS                              TYPESCRIPT       RXJS
==================  ==================================  ===============  ================
17.1.0              ^18.13.0 || ^20.9.0                 >=5.2.0 <5.4.0   ^6.5.3 || ^7.4.0
17.0.x              ^18.13.0 || ^20.9.0                 >=5.2.0 <5.3.0   ^6.5.3 || ^7.4.0
16.1.x || 16.2.x    ^16.14.0 || ^18.10.0                >=4.9.3 <5.2.0   ^6.5.3 || ^7.4.0
16.0.x              ^16.14.0 || ^18.10.0                >=4.9.3 <5.1.0   ^6.5.3 || ^7.4.0
15.1.x || 15.2.x    ^14.20.0 || ^16.13.0 || ^18.10.0    >=4.8.2 <5.0.0   ^6.5.3 || ^7.4.0
15.0.x              ^14.20.0 || ^16.13.0 || ^18.10.0    ~4.8.2           ^6.5.3 || ^7.4.0

Fonte: https://angular.io/guide/versions#actively-supported-versions