Si estás aprendiendo Angular, dominar los lifecycle hooks (ciclos de vida) es esencial para crear componentes eficientes y sin errores. En esta guía aprenderás:

  • Qué hace cada uno de los 9 hooks principales
  • Cuándo y cómo usar cada método con ejemplos reales
  • Las novedades de Angular 17: afterNextRender y afterRender
  • Errores comunes y cómo evitarlos

Al final de este post sabrás exactamente qué hook usar en cada situación. Vamos allá.

Cada método o hook, tiene un momento de ejecución durante el ciclo de vida del componente. La siguiente tabla te mostará de manera breve que hace cada método y estará clasificado según su fase.

FaseMétodoFunción
CreaciónconstructorSe ejecuta cuando Angular instancia al componente.
Detección de CambiosngOnChangesSe ejecuta cada vez que el @Input del componente ha recibido un cambio.
ngOnInitSe ejecuta una vez que Angular ha inicializado todos los @Inputs y se muestre el componente en la interfaz.
ngDoCheckSe ejecuta cada vez que va a verificar por si existe algún cambio.
ngAfterContentInitSe ejecuta una vez que el contenido del componente se ha inicializado.
ngAfterContentCheckedSe ejecuta cada vez que el contenido del componente va a verificar si existe algún cambio.
ngAfterViewInitSe ejecuta una vez después de inicializar la vista del componente.
ngAfterViewCheckedSe ejecuta cada vez que se comprueba si se han producido cambios en la vista del componente.
RenderizadoafterNextRenderSe ejecuta una vez la próxima vez que todos los componentes se han renderizado en el DOM.
afterRenderSe ejecuta cada vez que todos los componentes se han renderizado en el DOM.
DestrucciónngOnDestroySe ejecuta una vez antes de destruir el componente.

1. Constructor – Base del componente

Técnicamente no es un ciclo de vida. El constructor es el primer método en ejecutarse cuando se crea el componente.

El constructor se usa especificamente para preparar el componente para su uso. De esta manera asegura que todas las dependencias necesarias estén disponibles y que las propiedades básicas estén inicializadas.

import { Component } from '@angular/core';
import { MyService } from './my-service.service';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  myProperty: string;

  constructor(private myService: MyService) {
    console.log('Constructor llamado');
    // Aquí puedes usar myService

    // Inicialización de variables    
    this.myProperty = 'Valor inicial';
  }
}

Para cualquier lógica que dependa de la inicialización completa del componente o llamadas a servicios, es mejor usar  ngOnInit.

2. ngOnChanges – Detectar cambios en @Input

Este hook se ejecuta cada vez que un valor de entrada (@Input) cambia. El método se llama cada vez que cambia una de las propiedades enlazadas a datos de un componente.

Supongamos que quieres detectar los cambios en la url de una imagen y este dato es recibido por un @Input. Puedes utilizar ngOnChanges para captar esos cambios.

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-profile',
  template: `<img [src]="profileImage" alt="Profile Image">`
})
export class ProfileComponent implements OnChanges {
  @Input() profileImage: string;

  ngOnChanges(changes: SimpleChanges) {
    if (changes['profileImage']) {
      console.log('Profile image changed:', changes['profileImage'].currentValue);
      // Aquí puedes realizar cualquier acción adicional, como actualizar el UI
    }
  }
}

Te muestro otro ejemplo para de cómo ver el valor actual y el anterior.

@Input() profileImage: string;

ngOnChanges(changes: SimpleChanges) {
  if (changes['profileImage']) {
      console.log('Imagen anterior:', changes['profileImage'].previousValue);
      console.log('Imagen nueva:', changes['profileImage'].currentValue);
  }
}

Es ideal para reaccionar a cambios en los datos de entrada y realizar acciones basadas en esos cambios.

3. ngOnInit – Inicializar componente y llamar a servicios

El método ngOnInit se ejecuta cuando el componente se ha inicializado. El método es usado para inicializaciones y llamadas a servicios. Aquí se coloca la lógica de inicialización que depende de los enlaces de propiedades.

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

@Component({
  selector: 'app-ejemplo-on-init',
  template: `<p>Componente inicializado</p>`
})
export class EjemploOnInitComponent implements OnInit {
	data = null
	
	ngOnInit() {
	  this.dataService.getData().subscribe(
	    data => this.data = data
	  );
	}
}

4. ngDoCheck – Detector de cambios personalizado

Es similar a ngOnChanges, pero no es lo mismo. El hook ngDoCheck se encarga de detectar cambios, pero su enfoque es implementar una lógica personalizada y es ejecutado en cada ciclo de detección de cambios.

import { Component, Input, DoCheck, KeyValueDiffers, KeyValueDiffer } from '@angular/core';

@Component({
  selector: 'app-item-list',
  template: `<ul><li *ngFor="let item of items">{{ item }}</li></ul>`
})
export class ItemListComponent implements DoCheck {
  @Input() items: string[];
  private itemsDiffer: KeyValueDiffer<any, any>;

  constructor(private differs: KeyValueDiffers) {
    this.itemsDiffer = this.differs.find({}).create();
  }

  ngDoCheck() {
    const changes = this.itemsDiffer.diff(this.items);
    if (changes) {
      changes.forEachAddedItem(record => console.log('Added:', record.key, record.currentValue));
      changes.forEachRemovedItem(record => console.log('Removed:', record.key));
      // Aquí puedes realizar acciones adicionales basadas en los cambios detectados
    }
  }
}

Debes tener en cuenta que no puedes utilizar ambas propiedades a la vez o afectará al rendimiento de la aplicación. El hook es usado se ejecuta con frecuencia. Úsalo con precaución.

5. ngAfterContentInit – Verifica al componente hijo

El enfoque de ngAfterContentInit está en los componentes hijos del componente. Actua una vez para verificar al componente hijo e inicia después del ngDoCheck.

import { Component, AfterContentInit, ContentChild } from '@angular/core';

@Component({
  selector: 'app-ejemplo-after-content-init',
  template: `
    <ng-content></ng-content>
    <p>Contenido inicializado</p>
  `
})
export class EjemploAfterContentInitComponent implements AfterContentInit {

  @ContentChild('contenido') contenido;

  ngAfterContentInit() {
    console.log('Contenido inicializado:', this.contenido);
  }
}

6. ngAfterContentChecked – Ejecuta acciones después de la verificación

El lifecycle hook ngAfterContentChecked es útil para ejecutar acciones después de que se haya verificado el contenido proyectado. Siempre es ejecutado después del ngAfterContentInit y ngDoCheck.

Por ejemplo, puedes utilizarlo para realizar tareas adicionales de inicialización o para verificar y actualizar el estado del componente según el contenido proyectado.

import { Component, AfterContentChecked, ContentChild } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <div>
      <h2>Componente Padre</h2>
      <ng-content></ng-content>
    </div>
  `
})
export class ParentComponent implements AfterContentChecked {
  @ContentChild(ChildComponent) childComponent!: ChildComponent;

  ngAfterContentChecked() {
    console.log('ngAfterContentChecked llamado');
    // Aquí puedes realizar acciones adicionales
  }
}

7. ngAfterViewInit – Realiza acciones luego de inicializar todos los componentes

ngAfterViewInit se llama una vez cuando se ha inicializado la vista del componente y las vistas hijas. Esto ocurre después de ngAfterContentChecked. Es un buen lugar para realizar tareas que necesitan que todos los elementos de la vista estén disponibles.

Imagina que tienes un componente que necesita inicializar un gráfico utilizando una biblioteca de terceros como Chart.js. Necesitas asegurarte de que el elemento del DOM donde se renderizará el gráfico esté disponible antes de intentar inicializarlo.

import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
import { Chart } from 'chart.js';

@Component({
  selector: 'app-my-chart',
  template: `<canvas #chartCanvas></canvas>`
})
export class MyChartComponent implements AfterViewInit {
  @ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;

  ngAfterViewInit() {
    const ctx = this.chartCanvas.nativeElement.getContext('2d');
    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
          label: '# of Votes',
          data: [12, 19, 3, 5, 2, 3],
          backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(153, 102, 255, 0.2)',
            'rgba(255, 159, 64, 0.2)'
          ],
          borderColor: [
            'rgba(255, 99, 132, 1)',
            'rgba(54, 162, 235, 1)',
            'rgba(255, 206, 86, 1)',
            'rgba(75, 192, 192, 1)',
            'rgba(153, 102, 255, 1)',
            'rgba(255, 159, 64, 1)'
          ],
          borderWidth: 1
        }]
      },
      options: {
        scales: {
          y: {
            beginAtZero: true
          }
        }
      }
    });
  }
}

Es ideal para interactuar con elementos del DOM que no estaban disponibles en la inicialización del componente. Inicializar bibliotecas de terceros que requieren elementos renderizados, como gráficos, mapas, etc. O puedes configurar observadores o suscripciones que dependen de elementos de la vista.

8. ngAfterViewChecked – Realizaciones luego de mostrar los componentes y sus hijos

ngAfterViewChecked es un hook de Angular para realizar acciones después de que la vista del componente y sus hijos han sido verificados por el detector de cambios.

Se puede utilizar para:

  1. Detección de cambios manual: Puedes usar ngAfterViewChecked para realizar detección de cambios manual en casos específicos donde Angular no lo hace automáticamente.
  2. Interacción con elementos del DOM: Es útil para interactuar con elementos del DOM que dependen de la finalización de la verificación de la vista.
  3. Depuración: Puedes usarlo para depurar y verificar el estado de la vista después de cada ciclo de detección de cambios.

Vamos a ver un ejemplo: Supongamos que tienes un componente que muestra una lista de elementos y quieres resaltar un elemento específico después de que la vista ha sido verificada.

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

@Component({
  selector: 'app-my-component',
  template: `
    <div *ngFor="let item of items; let i = index" [class.highlight]="i === highlightedIndex">
      {{ item }}
    </div>
  `,
  styles: [`
    .highlight {
      background-color: yellow;
    }
  `]
})
export class MyComponent implements AfterViewChecked {
  items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
  highlightedIndex = 2;

  @ViewChild('highlightedElement', { static: false }) highlightedElement: ElementRef;

  ngAfterViewChecked() {
    if (this.highlightedElement) {
      this.highlightedElement.nativeElement.scrollIntoView({ behavior: 'smooth' });
    }
  }
}

En el template, hay una lista de elementos. Se añadirá una clase highlight al elemento para que se pueda resaltar. Luego con ngAfterViewChecked, después de que la vista ha sido verificada, se usa scrollIntoView para desplazar el elemento resaltado.

Consejo: Este método se llama repetidamente, por lo que es importante usarlo con cuidado para evitar problemas de rendimiento.

9. ngOnDestroy – Limpieza antes de destruir el componente

El último método, pero no el menos importante, se ejecuta justo antes de que Angular destruya el componente. Es útil para retirar a los observables y desconectar los event handlers para evitar memory leaks o fugas de memoria.

private subscription: Subscription;

ngOnInit() {
  this.subscription = this.dataService.getData().subscribe(
    data => this.data = data
  );
}

ngOnDestroy() {
  if (this.subscription) {
    this.subscription.unsubscribe();
  }
  console.log('Componente destruido');
}

Secuencia del Ciclo de Vida de un Componente en Angular

Angular 17: afterNextRender y afterNext

Con la llegada de Angular 17, tenemos 2 ciclos de vida nuevos: .

  • afterNextRender: Permite ejecutar el código justo después de renderizar todos los componentes en el DOM.
  • afterRender: Devuelve una llamada cada vez que el renderizado de la aplicación esté completa.

Ambos ciclos son útiles para tareas que dependen de que el DOM y que estén actualizadas. Por ejemplo, inicializar bibliotecas de terceros que requieren acceso al DOM o la realización de mediciones de rendimiento.

Preguntas frecuentes (FAQ)

1. ¿Cuál es la diferencia entre ngOnInit y el constructor?

El constructor es un método de TypeScript que se ejecuta cuando se instancia la clase del componente, y se usa principalmente para inicializar dependencias. En cambio, ngOnInit es un hook del ciclo de vida de Angular que se ejecuta después de que Angular ha inicializado todas las propiedades @Input del componente. Te recomiendo usar ngOnInit para lógica de inicialización, llamadas a servicios o cualquier operación que dependa de los datos del componente.

2. ¿Cuándo debo usar ngOnChanges en lugar de ngDoCheck?

ngOnChanges es más eficiente cuando necesitas detectar cambios en propiedades @Input específicas. Se ejecuta automáticamente cuando Angular detecta cambios en estas propiedades. Por otro lado, ngDoCheck te permite implementar tu propia lógica de detección de cambios, pero se ejecuta en cada ciclo de detección, lo que puede afectar el rendimiento. Mi recomendación: usa ngOnChanges siempre que sea posible y reserva ngDoCheck para casos donde necesites detectar cambios que Angular no puede detectar automáticamente.

3. ¿Por qué es importante usar ngOnDestroy?

ngOnDestroy es clave para evitar fugas de memoria (memory leaks) en tu aplicación. Cuando un componente se destruye pero sus suscripciones, temporizadores o event listeners siguen activos, estos continúan consumiendo recursos.

En ngOnDestroy debes cancelar todas las suscripciones, limpiar temporizadores y eliminar event listeners para liberar memoria y mantener tu aplicación eficiente.

4. ¿Puedo acceder al DOM en ngOnInit?

Aunque técnicamente puedes acceder al DOM en ngOnInit, no es recomendable porque las vistas del componente y sus hijos aún no están completamente inicializadas.

Si necesitas manipular elementos del DOM o inicializar bibliotecas de terceros que requieren elementos renderizados, es mejor usar ngAfterViewInit, que se ejecuta después de que la vista del componente está completamente inicializada.

5. ¿Cuál es la diferencia entre afterRender y afterNextRender en Angular 17?

afterNextRender se ejecuta solo una vez, la próxima vez que todos los componentes se hayan renderizado en el DOM. Es útil para tareas de inicialización única después del renderizado.

Por otro lado, afterRender se ejecuta cada vez que el renderizado de la aplicación está completo, lo que lo hace ideal para tareas que necesitan ejecutarse continuamente después de cada actualización del DOM.

Errores Comunes

1. Usar lógica pesada en ngDoCheck o ngAfterViewChecked

El problema: Estos hooks se ejecutan con mucha frecuencia durante cada ciclo de detección de cambios. Si incluyes operaciones costosas aquí, tu aplicación puede volverse lenta.

Solución: Mantén la lógica en estos hooks lo más ligera posible. Si necesitas realizar operaciones complejas, considera usar ngOnChanges o implementar tu propia estrategia de detección de cambios más eficiente.

2. No cancelar suscripciones en ngOnDestroy

El problema: Olvidar cancelar suscripciones a Observables, event listeners o temporizadores causa fugas de memoria que degradan el rendimiento de la aplicación con el tiempo.

Solución: Siempre implementa ngOnDestroy y cancela todas las suscripciones. Puedes usar takeUntil con un Subject para gestionar múltiples suscripciones de manera más elegante.

3. Manipular el DOM directamente en ngOnInit

El problema: Intentar acceder a elementos del DOM en ngOnInit puede fallar porque la vista aún no está completamente renderizada.

Solución: Usa ngAfterViewInit para manipulaciones del DOM o inicialización de bibliotecas que requieren elementos renderizados. Para Angular 17+, también puedes considerar afterNextRender o afterRender.

4. Confundir ngOnChanges con ngDoCheck

El problema: Usar ambos hooks simultáneamente o no entender cuándo usar cada uno puede llevar a código redundante y problemas de rendimiento.

Solución: Usa ngOnChanges para detectar cambios en @Input properties. Solo implementa ngDoCheck cuando necesites lógica personalizada de detección de cambios que Angular no puede manejar automáticamente.

5. Realizar llamadas HTTP en el constructor

El problema: Hacer peticiones HTTP en el constructor puede causar problemas porque el componente aún no está completamente inicializado y los @Input no están disponibles.

Solución: Realiza llamadas a servicios y peticiones HTTP en ngOnInit, donde tienes garantía de que el componente está inicializado correctamente.

Recomendaciones

  • Usa cada ciclo de vida para su propósito específico para mantener tu código organizado y eficiente.
  • Evita la lógica pesada en ngDoCheck y ngAfterViewChecked. Ambas se ejecutan con frecuencia.
  • Aprovecha ngOnInit para inicializaciones y ngOnDestroy para limpieza.
  • Usa ngOnChanges para reaccionar a cambios en los inputs de manera eficiente.
  • Experimenta con estos ciclos en tus componentes para entender mejor cómo funcionan en la práctica.

Te comparto este 👉 cheatsheet 👈 para que puedas repasar y tener el contenido resumido en la mano.

Conclusión

Cada hook tiene su propósito específico donde puede mejorar significativamente la forma de manejar la lógica de tus componentes, incluso mejorar la optimización de tu aplicación. Y con Angular 17, los nuevos ciclos afterNextRender y afterRender ofrecen un mejor control sobre el proceso de renderizado.

Se que no es fácil entender los ciclos de vida en Angular y sé que puede abrumar el contenido extenso. Date el tiempo para practicar, experimentar y probar cada uno de los ciclos. Recuerda que la clave está en utilizar cada hook de manera apropiada y eficiente.

Si esta guía te ha ayudado a entender mejor los ciclos de vida en Angular, me encantaría saber tu opinión. Déjame un comentario abajo compartiendo tu experiencia o cualquier duda que tengas.

No olvides compartir esta guía si te ha sido útil. Ayudará a otros desarrolladores a dominar Angular


Avatar de darkusphantom

👉 Únete a mi Canal de WhatsApp para más recursos

👉 Sigueme en mis redes sociales para más contenido de programación y productividad


darkusphantom Desarrollo Web , ,

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *