Si estás comenzando con Angular, entender los ciclos de vida de los componentes es fundamental para crear aplicaciones eficientes y bien estructuradas. He diseñado esta guia para explicar cada uno de los ciclos, con ejemplos prácticos y tips que te pueda ayudar.

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

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

Este ciclo 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

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

El hook ngDoCheck es ejecutado en cada ciclo de detección de cambios. El te permite implementar una lógica personalizada para la 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
    }
  }
}

Es similar a ngOnChanges, pero no es lo mismo.

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

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

Este hook es útil para ejecutar acciones después de que se haya verificado el contenido proyectado.

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
  }
}

Es ejecutado después del ngAfterContentInit y ngDoCheck.

7. ngAfterViewInit

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

ngAfterViewChecked en Angular es útil 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

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.

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.

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. 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.

¿Te ha resultado útil esta guía? Me encantaría saber tu opinión dejando un comentario abajo. Comparte tu experiencia con los ciclos de vida en Angular o cualquier duda que tengas.


Avatar de darkusphantom

Sigueme en mis redes sociales para más contenido


darkusphantom Desarrollo Web , ,

Deja una respuesta

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