¿Por qué document.querySelector no funciona en Angular?
\n\nImagine: está escribiendo su primera aplicación en Angular y necesita acceder a un <div> o <input> común directamente en la plantilla del componente. La primera idea es usar el clásico document.querySelector. Pero he aquí el problema: en la consola aparece null o undefined. ¿Le suena familiar?
Angular no es solo una biblioteca para manipular el DOM. Es un framework que trabaja con su propia abstracción sobre el DOM real. El acceso directo a los elementos a través de selectores globales viola la arquitectura del framework, puede provocar fugas de memoria y problemas de rendimiento. Pero la razón principal es que Angular utiliza un DOM virtual y Shadow DOM (dependiendo de la estrategia de encapsulación), por lo que su document.querySelector simplemente no ve los elementos dentro del componente.
Afortunadamente, los desarrolladores de Angular han previsto una solución elegante: los decoradores ViewChild y ViewChildren. En este artículo, analizaremos cómo seleccionar correctamente elementos en la plantilla de un componente usando TypeScript, y veremos todos los posibles inconvenientes.
\n\nFormas principales de acceder a los elementos de la plantilla
\n\nEn Angular existen tres enfoques principales para seleccionar elementos en la plantilla de un componente:
\n\n- \n
- Referencias de plantilla (Template Reference Variables) — la forma más sencilla para acceso rápido \n
- Decorador ViewChild — para acceder a un solo elemento o componente \n
- Decorador ViewChildren — para acceder a una colección de elementos del mismo tipo \n
Cada uno de estos enfoques tiene sus propias características y se aplica en diferentes escenarios. Veámoslos en detalle.
\n\n1. Referencias de plantilla: la forma más rápida
\n\nLas referencias de plantilla son un "truco" de Angular que permite acceder a un elemento DOM directamente en la plantilla, sin necesidad de escribir código en la clase del componente. Simplemente coloca #nombreReferencia en el elemento deseado.
// component.html\n<input #myInput type="text" placeholder="Ingrese texto">\n<button (click)="focusInput(myInput)">Enfocar input</button>\n\nEn este ejemplo, #myInput es una referencia de plantilla. Crea automáticamente una variable que se puede pasar a los métodos del componente. Observe: pasamos el elemento en sí, no su valor.
// component.ts\nimport { Component } from '@angular/core';\n\n@Component({\n selector: 'app-example',\n templateUrl: './example.component.html'\n})\nexport class ExampleComponent {\n focusInput(inputElement: HTMLInputElement): void {\n inputElement.focus();\n inputElement.style.borderColor = '#4CAF50';\n }\n}\n\nUn punto importante: las referencias de plantilla solo están disponibles dentro de la plantilla. No se pueden usar directamente en la clase del componente. Para eso se necesitan los decoradores.
\n\n2. ViewChild: acceso a un solo elemento
\n\nEl decorador @ViewChild es la herramienta principal para obtener acceso a un elemento o componente hijo desde la clase del componente. Funciona basándose en un selector: puede buscar por referencia de plantilla, por tipo de componente o por selector CSS (con limitaciones).
// component.ts\nimport { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';\n\n@Component({\n selector: 'app-example',\n template: `\n <div #myDiv>Este elemento lo seleccionaremos</div>\n <p #myParagraph>Y este también</p>\n `\n})\nexport class ExampleComponent implements AfterViewInit {\n @ViewChild('myDiv') myDiv!: ElementRef;\n @ViewChild('myParagraph') myParagraph!: ElementRef;\n\n ngAfterViewInit(): void {\n // ¡Importante! El acceso a los elementos solo es posible después de la inicialización de la vista\n console.log(this.myDiv.nativeElement.textContent); // "Este elemento lo seleccionaremos"\n this.myPara