A menudo me hacen consultas originadas por una mala comprensión de los ciclos de vida de componentes y directivas. Este artículo es la respuesta a todas esas dudas 😉
Ciclo de vida en Angular
Creencias religiosas aparte, me atrevo a decir que si los componentes de tu web fueran personas, Angular vendría a ser Dios.
Me explico:
Angular crea los componentes, los renderiza, crea y renderiza a sus hijos, actualiza y re-renderiza los componentes cuando detecta cambios y finalmente los destruye antes de eliminarlos de la faz de la tierra del DOM.
Todo el ciclo de vida de los componentes, está gestionado por Dios Angular 🙂
Y para ayudarte a trabajar con los componentes, Angular te da acceso a los momentos clave de su vida a través de unos callbacks, los denominados lifecycle hooks.
Esto mismo ocurre con las directivas, por cierto. Al fin y al cabo, un componente no deja de ser un caso particular de una directiva.
Secuencia de ciclo de vida
El punto de partida de un componente (su creación), es el momento en que Angular llama a su constructor.
La creación en sí no necesita ningún hook, ya que puedes actuar directamente sobre su constructor. Pero es el pistoletazo de salida que activa todo el ciclo.
A partir de este momento se empiezan a llamar los lifecycle hooks del componente, o mejor dicho, sólo aquellos que has implementado.
Se llaman siguiendo la secuencia que ves en la imagen:
Todos los lifecycle hooks llevan, por cierto, el prefijo ng delante (ngOnChanges
, ngOnInit
, etc).
¿Qué hace cada uno? Eso te lo contestaré en breve, ten paciencia.
De momento, déjame explicarte como se usan: Lo único que realmente necesitas hacer, es implementar en el componente el método del hook que te interesa. Así:
import { Component } from '@angular/core';
@Component({
selector: 'app-foo'
})
export class FooComponent {
constructor() { }
ngOnInit() {
//do something here
}
}
Ciclo de detección de cambios
Te habrás dado cuenta de que la secuencia de mi diagrama seguía una estructura un poco extraña.
El motivo es muy simple: Una vez listo el componente (tras ejecutar toda la secuencia salvo el ngOnDestroy()
), los callbacks del centro se llaman cíclicamente.
Es lo que se conoce como ciclo de detección de cambios.
¿Y las interfaces de los hooks?
Si usas la CLI de Angular para crear componentes, te habrás dado cuenta de que de entrada te genera el ngOnInit()
y aplica la interfaz OnInit
al componente, como puedes ver a continuación:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-foo'
})
export class FooComponent implements OnInit {
constructor() { }
ngOnInit() {
//do something here
}
}
Ese OnInit
es simplemente una interfaz. Un «contrato» que obliga a cumplir una cierta estructura (en este caso, incluir el método ngOnInit()
) a la clase que lo implementa.
En realidad no es obligatorio que uses las interfaces asociadas a los lifecycle hooks. Angular ejecutará los hooks que implementes, ya sea con, o sin interfaces.
Entonces… ¿para qué sirven?
Pues aunque no son necesarios, es una buena práctica que te ayudará a prevenir errores (como por ejemplo no implementar un hook que realmente necesitas) y a beneficiarte de las herramientas de tipado de TS.
¿Qué hace cada lifecycle hook?
Los nombres son bastante indicativos, pero no está de más aclararlo punto a punto. A continuación tienes unas slides donde se va construyendo el diagrama de antes, y se explica lo que hace cada hook.
Te dejo también el detalle de eventos, siguiendo la secuencia del ciclo de vida:
ngOnChanges()
: Este hook se llama al inicio y cada vez que Angular detecta un cambio en los inputs del componente (cuando tienes algún property binding). Recibe como parámetro un objetoSimpleChanges
, con los valores actuales y anteriores (si había) de los inputs.-
ngOnInit()
: Este hook te permite inicializar el componente una vez ha recibido las propiedades de entrada. -
ngDoCheck()
: Sirve para detectar y actuar sobre cambios que Angular no va a detectar por si mismo. Se llama también durante cada ciclo de detección de cambios, después dengOnChanges()
. -
ngAfterContentInit()
: Se ejecuta una sola vez, justo después de que Angular proyecte contenido externo en la vista del componente (conng-content
). -
ngAfterContentChecked()
: Se ejecuta después de que Angular compruebe el contenido proyectado en el componente. Se ejecuta también durante los ciclos de detección de cambios, después dengDoCheck()
. -
ngAfterViewInit()
: Se llama una única vez, tras inicializar las vistas y sub-vistas del componente. -
ngAfterViewChecked()
: Se llama después de comprobar los cambios de las vistas y sub-vistas del componente. Se ejecuta también durante el ciclo de detección de cambios, después dengAfterContentChecked()
. -
ngOnDestroy()
: Se llama solo una vez, justo antes de que Angular destruya el componente, y sirve para prevenir memory leaks, eliminando por ejemplo suscripciones a Observables e event handlers.
Reflexiones personales
Los lifecycle hooks son un mecanismo muy simple, pero es esencial entenderlos bien para poder exprimir al máximo la manipulación de componentes Angular.
Por ejemplo, si trabajas con content projection, seguramente necesitarás echar mano del ngAfterContentInit()
.
Otro ejemplo: si quieres acceder al contenido que renderiza tu componente, es importante que conozcas ngAfterViewInit()
.
Y por supuesto, si trabajas con una estrategia de detección de cambios OnPush
, deberías tener bien claros los hooks que participan en el ciclo de detección de cambios.
¿Te ha gustado este artículo? No te cortes, déjame un comentario y ayúdame a compartirlo 😉