Angular

Angular

Tutorial Angular: Ciclo de vida de componentes en Angular

Angular: ciclo de vida de componentes y hooks. Explora las interfaces y métodos asociados con los hooks del ciclo de vida de los componentes en Angular para un desarrollo eficiente.

Los componentes en Angular tienen un ciclo de vida que comienza con la creación del componente y termina con su destrucción.

Durante el ciclo de vida de un componente, Angular invoca varios métodos del componente en momentos específicos, conocidos como métodos del ciclo de vida.

Estos métodos permiten a los desarrolladores realizar acciones en diferentes etapas del ciclo de vida del componente.

Creación de un componente

Constructor

En esta etapa, se crea la instancia del componente y se inicializan sus propiedades. 

El constructor es una función especial de TypeScript que se llama automáticamente al crear una instancia de la clase. Es el primer método que se ejecuta en el ciclo de vida del componente.

El constructor es ideal para inyectar dependencias y realizar configuraciones iniciales, como suscribirse a servicios, inicializar variables, o configurar propiedades del componente. 

Sin embargo, es importante tener en cuenta que en el constructor no se deben ejecutar tareas que dependen de la vista del componente, ya que esta aún no ha sido inicializada.

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

@Component({
  selector: 'app-example',
  template: '<p>{{ message }}</p>'
})
export class ExampleComponent implements OnInit {
  message: string;

  constructor() {
    this.message = 'Hello, Angular!';
    console.log('Constructor executed');
  }

  ngOnInit(): void {
    console.log('ngOnInit executed');
  }
}

OnInit

En esta etapa, después de que se haya inicializado el componente, se ejecuta el método ngOnInit().

Se utiliza para tareas de inicialización adicionales y lógica que debe realizarse después de que todas las propiedades del componente estén listas.

Es un buen lugar para inicializar datos que provienen de servicios externos.

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

export class ExampleComponent implements OnInit, OnChanges {
  @Input() name: string;
  message: string;

  constructor() {
    this.message = 'Hello, Angular!';
    console.log('Constructor executed');
  }

  ngOnInit(): void {
    console.log('ngOnInit executed');
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log('ngOnChanges executed', changes);
  }
}

OnChanges

Cuando una propiedad de entrada del componente cambia, el método ngOnChanges() se ejecuta.

Es útil para reaccionar a los cambios en las propiedades de entrada (@Input) del componente.

Este método recibe un objeto SimpleChanges que contiene los cambios de las propiedades de entrada.

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

export class ExampleComponent implements OnChanges {
  @Input() name: string | undefined;

  ngOnChanges(changes: SimpleChanges): void {
    console.log('ngOnChanges executed', changes);
    // Ejemplo de reacción a cambios en propiedades de entrada
    if (changes['name']) {
      console.log('Name changed:', changes['name'].currentValue);
    }
  }
}

Actualización de un componente

Cuando hay cambios en los datos o en la vista, Angular revisa el componente y realiza las actualizaciones necesarias.

Los métodos relevantes en esta fase son:

DoCheck

En esta etapa, Angular verifica manualmente los cambios que no ha detectado automáticamente.

Puede ser utilizada para realizar tareas de detección de cambios personalizadas.

Este método se llama con mucha frecuencia, por lo que es importante que su implementación sea eficiente para evitar problemas de rendimiento.

import { Component, DoCheck, Input, IterableDiffers, IterableDiffer, IterableChangeRecord } from '@angular/core';

@Component({
  selector: 'app-example',
  standalone: true,
  template: `
    <div>
        <p>{{ message }}</p>
        <button (click)="addItem()">Add Item</button>
        <button (click)="removeItem()">Remove Item</button>
        <button (click)="changeMessage()">Change Message</button>
        @if (items.length) {
            <ul>
            @for (item of items; track $index) {
                <li>{{ item }}</li>
            }
            </ul>
        } @else {
            <p>No items to display.</p>
        }
    </div>
  `
})
export class ExampleComponent implements DoCheck {
   @Input() items: string[] = ['Item 1', 'Item 2', 'Item 3'];
  @Input() message: string = 'Hello, Angular!';

  private itemsDiffer: IterableDiffer<any>;
  private previousMessage: string;

  constructor(private differs: IterableDiffers) {
    this.itemsDiffer = this.differs.find(this.items).create();
    this.previousMessage = this.message;
  }

  ngDoCheck(): void {
    console.log('ngDoCheck executed');
    
    // Detectar cambios en la lista de items
    const itemsChanges = this.itemsDiffer.diff(this.items);
    if (itemsChanges) {
      itemsChanges.forEachAddedItem((record: IterableChangeRecord<any>) => {
        console.log('Item added:', record.item);
      });
      itemsChanges.forEachRemovedItem((record: IterableChangeRecord<any>) => {
        console.log('Item removed:', record.item);
      });
    }

    // Detectar cambios en la propiedad message
    if (this.message !== this.previousMessage) {
      console.log('Message changed from', this.previousMessage, 'to', this.message);
      this.previousMessage = this.message;
    }
  }

  addItem() {
    this.items.push(`Item ${this.items.length + 1}`);
  }

  removeItem() {
    this.items.pop();
  }

  changeMessage() {
    this.message = 'Message changed from ExampleComponent';
  }
}

AfterContentInit

Una vez que el contenido proyectado (contenido dentro de las etiquetas <ng-content>) se ha inicializado, se ejecuta el método ngAfterContentInit().

Este método es útil para realizar tareas que dependen del contenido proyectado en el componente.

// parent.component.ts
import { Component, AfterContentInit, ContentChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-parent',
  standalone: true,
  template: `
    <div>
      <ng-content></ng-content>
      @if (contentInitialized) {
        <p>Contenido proyectado: {{ projectedText }}</p>
      }
    </div>
  `
})
export class ParentComponent implements AfterContentInit {
  @ContentChild('projectedContent', { static: true }) content!: ElementRef;
  
  contentInitialized = false;
  projectedText = '';

  ngAfterContentInit(): void {
    console.log('ngAfterContentInit ejecutado');
    this.projectedText = this.content.nativeElement.textContent;
    this.contentInitialized = true;
  }
}

// example.component.ts
@Component({
  selector: 'app-example',
  standalone: true,
  imports: [ParentComponent],
  template: `
    <app-parent>
      <p #projectedContent>
        @if (showContent) {
          Contenido proyectado dinámico
        } @else {
          Contenido alternativo
        }
      </p>
    </app-parent>
    <button (click)="toggleContent()">Cambiar contenido</button>
  `
})
export class ExampleComponent {
  showContent = true;

  toggleContent() {
    this.showContent = !this.showContent;
  }
}

En este ejemplo se muestra a no solo el uso de AfterContentInit, sino también cómo el contenido proyectado puede cambiar dinámicamente y cómo el componente padre puede reaccionar a estos cambios. 

AfterContentChecked

Después de cada verificación del contenido proyectado, se ejecuta el método ngAfterContentChecked().

Este método se llama después de cada ciclo de detección de cambios y puede utilizarse para realizar acciones adicionales después de que Angular haya verificado el contenido proyectado.

// parent.component.ts
import { Component, AfterContentChecked, ContentChild, ElementRef, signal } from '@angular/core';

@Component({
  selector: 'app-parent',
  standalone: true,
  template: `
    <div>
      <ng-content></ng-content>
      @if (contentChecked()) {
        <p>Último contenido verificado: {{ lastCheckedContent() }}</p>
      }
    </div>
  `
})
export class ParentComponent implements AfterContentChecked {
  @ContentChild('projectedContent', { static: true }) content!: ElementRef;

  contentChecked = signal(false);
  lastCheckedContent = signal('');

  ngAfterContentChecked(): void {
    console.log('ngAfterContentChecked ejecutado');
    const currentContent = this.content.nativeElement.textContent;
    console.log('Contenido proyectado verificado:', currentContent);
    
    this.lastCheckedContent.set(currentContent);
    this.contentChecked.set(true);
  }
}

// example.component.ts
@Component({
  selector: 'app-example',
  standalone: true,
  imports: [ParentComponent],
  template: `
    <app-parent>
      <p #projectedContent>{{ dynamicContent() }}</p>
    </app-parent>
    <button (click)="updateContent()">Actualizar contenido</button>
  `
})
export class ExampleComponent {
  dynamicContent = signal('Contenido proyectado inicial');
  updateCount = 0;

  updateContent() {
    this.updateCount++;
    this.dynamicContent.set(`Contenido proyectado actualizado ${this.updateCount} veces`);
  }
}

En este ejemplo el componente padre (ParentComponent) verifica y muestra el contenido proyectado cada vez que cambia, utilizando señales para manejar el estado de forma reactiva. El componente hijo (ExampleComponent) permite actualizar dinámicamente el contenido proyectado, lo que desencadena la verificación en el padre.

AfterViewInit

Una vez que la vista y los elementos hijos se han inicializado, se ejecuta el método ngAfterViewInit()

Es útil para realizar acciones que dependen de las vistas de los componentes hijos, como la configuración de elementos del DOM o la inicialización de bibliotecas que requieren acceso al DOM.

// child.component.ts
import { Component, AfterViewInit, ViewChild, ElementRef, signal } from '@angular/core';

@Component({
  selector: 'app-child',
  standalone: true,
  template: `<p>{{ message() }}</p>`
})
export class ChildComponent {
  message = signal('Componente hijo inicial');

  updateMessage(newMessage: string) {
    this.message.set(newMessage);
  }
}

// parent.component.ts
@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [ChildComponent],
  template: `
    <app-child #childComponent />
    <button (click)="updateChildMessage()">Actualizar hijo</button>
    @if (childInitialized()) {
      <p>Estado del hijo: {{ childState() }}</p>
    }
  `
})
export class ParentComponent implements AfterViewInit {
  @ViewChild('childComponent') child!: ChildComponent;
  
  childInitialized = signal(false);
  childState = signal('');

  ngAfterViewInit(): void {
    console.log('ngAfterViewInit ejecutado');
    console.log('Componente hijo:', this.child);
    this.childInitialized.set(true);
    this.updateChildState();
  }

  updateChildMessage() {
    this.child.updateMessage('Mensaje actualizado del hijo');
    this.updateChildState();
  }

  private updateChildState() {
    this.childState.set(this.child.message());
  }
}

En este ejemplo el componente padre (ParentComponent) accede y manipula el componente hijo (ChildComponent) después de su inicialización. Utiliza señales para manejar el estado de forma reactiva y muestra cómo interactuar con las vistas de los componentes hijos una vez inicializadas.

AfterViewChecked

Después de cada verificación de la vista y los elementos hijos, se ejecuta el método ngAfterViewChecked().

Este método se llama después de cada ciclo de detección de cambios y puede utilizarse para realizar acciones adicionales después de que Angular haya verificado la vista del componente.

// child.component.ts
import { Component, AfterViewChecked, ViewChild, signal, effect } from '@angular/core';

@Component({
  selector: 'app-child',
  standalone: true,
  template: `<p>{{ counter() }}</p>`
})
export class ChildComponent {
  counter = signal(0);

  increment() {
    this.counter.update(value => value + 1);
  }
}

// parent.component.ts
@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [ChildComponent],
  template: `
    <app-child #childComponent />
    <button (click)="incrementChild()">Incrementar hijo</button>
    <p>Última verificación: {{ lastCheckedValue() }}</p>
  `
})
export class ParentComponent implements AfterViewChecked {
  @ViewChild('childComponent') child!: ChildComponent;
  
  lastCheckedValue = signal(0);
  checkCount = signal(0);

  constructor() {
    effect(() => {
      console.log(`Vista verificada ${this.checkCount()} veces. Último valor: ${this.lastCheckedValue()}`);
    });
  }

  ngAfterViewChecked(): void {
    this.checkCount.update(count => count + 1);
    this.lastCheckedValue.set(this.child.counter());
  }

  incrementChild() {
    this.child.increment();
  }
}

En este ejemplo el componente padre (ParentComponent) verifica el estado del componente hijo (ChildComponent) después de cada ciclo de detección de cambios. Utiliza señales y efectos para manejar y mostrar de forma reactiva el estado de las verificaciones.

Destrucción de un componente

Cuando un componente ya no es necesario, Angular lo destruye para liberar recursos.

OnDestroy

El método ngOnDestroy() se llama justo antes de que Angular destruya el componente.

Este método es ideal para realizar tareas de limpieza, como desuscribirse de observables, eliminar temporizadores o liberar recursos que el componente esté utilizando.

import { Component, OnDestroy, Input, inject } from '@angular/core';
import { Subscription, interval } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DataService } from './data.service';

@Component({
  selector: 'app-example',
  standalone: true,
  template: `
    <div>
      <p>{{ message }}</p>
      @if (items.length) {
        <ul>
          @for (item of items; track $index) {
            <li>{{ item }}</li>
          }
        </ul>
      } @else {
        <p>No hay elementos para mostrar.</p>
      }
    </div>
  `
})
export class ExampleComponent implements OnDestroy {
  @Input() items: string[] = [];
  @Input() message = 'Hello, Angular!';
  
  private dataService = inject(DataService);
  private intervalSubscription: Subscription;

  constructor() {
    // Suscripción a un observable del servicio usando takeUntilDestroyed
    this.dataService.getData().pipe(
      takeUntilDestroyed()
    ).subscribe(data => {
      this.items = data;
    });

    // Configuración de un intervalo usando RxJS
    this.intervalSubscription = interval(1000).pipe(
      takeUntilDestroyed()
    ).subscribe(() => {
      console.log('Intervalo ejecutado');
    });
  }

  ngOnDestroy(): void {
    console.log('ngOnDestroy ejecutado');
    
    // La suscripción al servicio de datos se maneja automáticamente por takeUntilDestroyed

    // Limpiar suscripción del intervalo (por si acaso)
    if (this.intervalSubscription) {
      this.intervalSubscription.unsubscribe();
      console.log('Suscripción del intervalo cancelada');
    }
  }
}

En este ejemplo se utiliza takeUntilDestroyed() para manejar automáticamente la cancelación de suscripciones, simplificando la limpieza de recursos. El componente se suscribe a un servicio de datos y configura un intervalo, demostrando cómo gestionar eficientemente los recursos y evitar fugas de memoria en la destrucción del componente.

Comparación de métodos similares del ciclo de vida

Constructor vs OnInit

  - Constructor: Se llama cuando se crea la instancia del componente. Es ideal para inyectar dependencias, pero no para inicializar datos.
  - OnInit: Se ejecuta una vez que Angular ha inicializado todas las propiedades vinculadas a datos. Es el lugar recomendado para inicializar datos y realizar configuraciones iniciales.

OnChanges vs DoCheck

  - OnChanges: Se ejecuta cuando Angular detecta cambios en las propiedades de entrada (@Input). Es eficiente para reaccionar a cambios específicos en las entradas del componente.
  - DoCheck: Se ejecuta durante cada ciclo de detección de cambios, permitiendo una detección de cambios personalizada. Es más flexible pero menos eficiente, por lo que debe usarse con precaución.

AfterContentInit vs AfterViewInit

  - AfterContentInit: Se ejecuta después de que Angular proyecta el contenido externo en la vista del componente. Es útil para inicializar algo que depende del contenido proyectado.
  - AfterViewInit: Se ejecuta después de que Angular inicializa la vista del componente y sus vistas hijas. Es ideal para realizar acciones que requieren que los elementos del DOM estén completamente renderizados.

AfterContentChecked vs AfterViewChecked

  - AfterContentChecked: Se llama después de cada verificación del contenido proyectado. Útil para detectar cambios en el contenido proyectado después de cada ciclo de detección.
  - AfterViewChecked: Se llama después de cada verificación de la vista del componente y sus vistas hijas. Permite realizar acciones adicionales después de que Angular actualiza el DOM.

Certifícate en Angular con CertiDevs PLUS

Ejercicios de esta lección Ciclo de vida de componentes en Angular

Evalúa tus conocimientos de esta lección Ciclo de vida de componentes en Angular con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Signals en Angular

Angular
Puzzle

Guards funcionales

Angular
Test

Decodificar JWT en Angular

Angular
Test

Servicio con HttpClient

Angular
Código

Ciclo de vida de componentes en Angular

Angular
Test

Gestión de productos de Fake Store API

Angular
Proyecto

Data binding en Angular

Angular
Test

Routes sin módulos en Angular

Angular
Código

Router en Angular

Angular
Test

Instalación de Angular

Angular
Puzzle

Route Guards basados en interfaces

Angular
Código

La directiva @if en Angular

Angular
Puzzle

Formularios reactivos en Angular

Angular
Código

Servicios en Angular

Angular
Puzzle

Interceptor funcional

Angular
Test

Servicio con Array

Angular
Código

La directiva @for en Angular

Angular
Puzzle

Interceptores HTTP

Angular
Código

Componentes standalone true

Angular
Puzzle

Formularios con ngModel en Angular

Angular
Puzzle

Routes en Angular

Angular
Test

Comunicación entre componentes Angular

Angular
Test

Parámetros en rutas con ActivatedRoute

Angular
Test

CRUD de Restaurantes y Platos

Angular
Proyecto

Tablas en Angular Material

Angular
Puzzle

Formulario de registro de usuarios

Angular
Proyecto

Instalación y uso de NgBoostrap

Angular
Puzzle

Desarrollo de componentes Angular

Angular
Test

JWT en Angular

Angular
Código

Formularios reactivos en Angular

Angular
Puzzle

Formularios en Angular Material

Angular
Puzzle

Layout con Angular Material

Angular
Puzzle

Effects en Angular

Angular
Test

Data binding

Angular
Código

HttpClient en servicios de Angular

Angular
Puzzle

Desarrollo de módulos Angular

Angular
Puzzle

Comandos Angular CLI

Angular
Puzzle

Subir archivo en formularios

Angular
Test

La directiva routerLink en Angular

Angular
Test

Todas las lecciones de Angular

Accede a todas las lecciones de Angular y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación Angular

Angular

Introducción Y Entorno

Comandos Angular Cli

Angular

Introducción Y Entorno

Desarrollo De Componentes Angular

Angular

Componentes

Data Binding En Angular

Angular

Componentes

Ciclo De Vida De Componentes En Angular

Angular

Componentes

Comunicación Entre Componentes Angular

Angular

Componentes

La Directiva @If En Angular

Angular

Componentes

La Directiva @For En Angular

Angular

Componentes

Componentes Standalone

Angular

Componentes

Desarrollo De Módulos Angular

Angular

Módulos

Routes En Angular

Angular

Enrutado Y Navegación

Router En Angular

Angular

Enrutado Y Navegación

La Directiva Routerlink En Angular

Angular

Enrutado Y Navegación

Parámetros En Rutas Con Activatedroute

Angular

Enrutado Y Navegación

Routes Sin Módulos En Angular

Angular

Enrutado Y Navegación

Servicios En Angular

Angular

Servicios E Inyección De Dependencias

Httpclient En Servicios De Angular

Angular

Servicios E Inyección De Dependencias

Formularios Con Ngmodel En Angular

Angular

Formularios

Formularios Reactivos En Angular

Angular

Formularios

Subir Archivo En Formularios

Angular

Formularios

Layout Con Angular Material

Angular

Integración Con Angular Material

Tablas En Angular Material

Angular

Integración Con Angular Material

Formularios En Angular Material

Angular

Integración Con Angular Material

Instalación Y Uso De Ngboostrap

Angular

Integración Con Bootstrap Css

Signals En Angular

Angular

Signals Y Reactividad

Effects En Angular

Angular

Signals Y Reactividad

Route Guards Basados En Interfaces

Angular

Autenticación Y Autorización

Guards Funcionales

Angular

Autenticación Y Autorización

Interceptores Http Basados En Interfaz

Angular

Autenticación Y Autorización

Interceptores Http Funcionales

Angular

Autenticación Y Autorización

Seguridad Jwt En Angular

Angular

Autenticación Y Autorización

Decodificar Tokens Jwt En Angular

Angular

Autenticación Y Autorización

Certificados de superación de Angular

Supera todos los ejercicios de programación del curso de Angular y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender el ciclo de vida de un componente en Angular.
  2. Dominar los métodos del ciclo de vida de un componente.
  3. Identificar y diferenciar entre las diversas etapas del ciclo de vida de un componente.