Ciclo di vita dei componenti Angular: guida completa ai lifecycle hooks

banner

Il ciclo di vita dei componenti è uno dei concetti fondamentali da padroneggiare per sviluppare applicazioni Angular efficaci. In questa guida esploreremo tutti i lifecycle hooks disponibili, il loro ordine di esecuzione e come utilizzarli in scenari reali.

Cos’è il ciclo di vita di un componente

Ogni componente Angular attraversa diverse fasi durante la sua esistenza: creazione, inizializzazione, rilevamento dei cambiamenti e distruzione. Angular fornisce degli “hook” (tradotto in “ganci”, nel senso di “dritta”) che permettono di intercettare questi momenti specifici ed eseguire logica personalizzata.

Lifecycle Hooks

ngOnChanges

Quando viene chiamato: Prima di ngOnInit, dopo il costruttore, e ogni volta che cambiano le proprietà di input del componente. In altre parole, viene spesso utilizzato con le proprietà decorate con @Input.

Scopo: Gestire i cambiamenti delle proprietà di input e reagire di conseguenza.

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

@Component({
  selector: 'app-user-profile',
  template: `
    <div>
      <h2>{{userName}}</h2>
      <p>Ultimo aggiornamento: {{lastUpdate}}</p>
    </div>
  `
})
export class UserProfileComponent implements OnChanges {
  @Input() userName: string = '';
  lastUpdate: string = '';

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['userName']) {
      console.log('Username cambiato:', changes['userName'].currentValue);
      this.lastUpdate = new Date().toLocaleTimeString();
    }
  }
}

ngOnInit

Quando viene chiamato: Una sola volta, dopo il primo ngOnChanges.

Scopo: Inizializzazione del componente, caricamento di dati iniziali.

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngIf="loading">Caricamento...</div>
    <ul *ngIf="!loading">
      <li *ngFor="let user of users">{{user.name}}</li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users: any[] = [];
  loading = true;

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.loadUsers();
  }

  private loadUsers(): void {
    this.userService.getUsers().subscribe({
      next: (users) => {
        this.users = users;
        this.loading = false;
      },
      error: (error) => {
        console.error('Errore nel caricamento utenti:', error);
        this.loading = false;
      }
    });
  }
}

ngDoCheck

Quando viene chiamato: Durante ogni ciclo di rilevamento dei cambiamenti.

Scopo: Implementare la logica che rileva eventuali cambiamenti che Angular non rileva automaticamente.

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

@Component({
    selector: 'app-custom-detector',
    template: `<p>Elementi nell'array: {{items.length}}</p>`
})
export class CustomDetectorComponent implements DoCheck {
    @Input() items: any[] = [];
    private lastItemsLength = 0;

    ngDoCheck(): void {
        if (this.items.length !== this.lastItemsLength) {
            console.log('Cambiamento rilevato nell\'array items');
            this.lastItemsLength = this.items.length;
        }
    }
}

ngAfterContentInit

Quando viene chiamato: Dopo che Angular ha caricato completamente il contenuto esterno nel componente, ossia il contenuto proiettato con <ng-content>.

Scopo: Gestire la logica che dipende dal contenuto, e quindi dalla proiezione di contenuti. Per contenuto si intende ciò che viene inserito all’interno del componente da un altro componente, e non il template del componente stesso.

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

@Component({
    selector: 'app-content-wrapper',
    template: `
    <div class="wrapper">
      <ng-content></ng-content>
    </div>
  `
})
export class ContentWrapperComponent implements AfterContentInit {
    @ContentChild('projectedElement') projectedElement!: ElementRef;

    ngAfterContentInit(): void {
        if (this.projectedElement) {
            console.log('Elemento nella vista inizializzato:', this.projectedElement.nativeElement);
        }
    }
}

ngAfterViewInit

Quando viene chiamato: Dopo che Angular ha inizializzato completamente la view del componente e le view dei componenti figli.

Scopo: Manipolazione del DOM, inizializzazione di librerie esterne.

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

@Component({
    selector: 'app-chart',
    template: `<canvas #chartCanvas width="400" height="200"></canvas>`
})
export class ChartComponent implements AfterViewInit {
    @ViewChild('chartCanvas') canvas!: ElementRef<HTMLCanvasElement>;

    ngAfterViewInit(): void {
        this.initializeChart();
    }

    private initializeChart(): void {
        const ctx = this.canvas.nativeElement.getContext('2d');
        if (ctx) {
            // Inizializzazione di una libreria di grafici come Chart.js
            console.log('Canvas pronto per il rendering del grafico');
        }
    }
}

ngAfterViewInit vs ngAfterContentInit: Il primo si occupa della view del componente e dei suoi figli, mentre il secondo si occupa del contenuto proiettato nel componente. Nella pratica, ngAfterViewInit viene usato più frequentemente per manipolare il DOM, mentre ngAfterContentInit è utile quando si lavora con componenti che utilizzano <ng-content>.

ngOnDestroy

Quando viene chiamato: Poco prima che Angular distrugga il componente.

Scopo: Pulizia delle risorse, cancellazione di subscriptions, timer, event listeners.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription, interval } from 'rxjs';

@Component({
    selector: 'app-timer',
    template: `<p>Timer: {{counter}}</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
    counter = 0;
    private timerSubscription?: Subscription;

    ngOnInit(): void {
        this.timerSubscription = interval(1000).subscribe(() => {
            this.counter++;
        });
    }

    ngOnDestroy(): void {
        // Importante: cancellare la subscription per evitare memory leaks
        if (this.timerSubscription) {
            this.timerSubscription.unsubscribe();
        }
    }
}

Ordine di esecuzione dei Lifecycle Hooks

Questi hooks hanno un ordine specifico di esecuzione durante il ciclo di vita del componente:

  1. ngOnChanges (se ci sono input properties)
  2. ngOnInit
  3. ngDoCheck
  4. ngAfterContentInit
  5. ngAfterContentChecked
  6. ngAfterViewInit
  7. ngAfterViewChecked
  8. ngDoCheck (ripetuto ad ogni change detection)
  9. ngAfterContentChecked (ripetuto ad ogni change detection)
  10. ngAfterViewChecked (ripetuto ad ogni change detection)
  11. ngOnDestroy (alla distruzione del componente)

Rappresentazione grafica del ciclo di vita degli hooks in Angular (Credits: https://miloszeljko.com)

Come visibile anche dall’immagine, ci sono una serie di hooks che vengono richiamati ad ogni ciclo di change detection (ngDoCheck, ngAfterContentChecked, ngAfterViewChecked). Questi hooks sono utili per eseguire logiche che devono essere verificate frequentemente, ma è importante usarli con cautela per evitare problemi di performance, considerato che ogni cambiamento nello stato dell’applicazione (ad esempio un evento utente o una risposta HTTP) può innescare un ciclo di change detection.

Post correlati

TheRedCode.it - Il mondo #tech a piccoli #bit

Partners

Community, aziende e persone che supportano attivamente il blog

Logo di Codemotion
Logo di GrUSP
Logo di Python Milano
Logo di Schrodinger Hat
Logo di Python Biella Group
Logo di Fuzzy Brains
Logo di Django Girls
Logo di Improove
Logo del libro open source
Logo di NgRome
Logo de La Locanda del Tech
Logo di Tomorrow Devs
Logo di DevDojo
Logo di Cloud Native Days 2025

Non perderti gli ultimi aggiornamenti, iscriviti a TheRedCode Digest!

La tecnologia corre, e tu devi correre più veloce per rimanere sempre sul pezzo! 🚀

Riceverai una volta al mese (o anche meno) con codici sconto per partecipare agli eventi del settore, quiz per vincere dei gadget e i recap degli articoli più interessanti pubblicati sul blog

Ci sto!

Vuoi diventare #tech content creator? 🖊️

Se vuoi raccontare la tua sul mondo #tech con dei post a tema o vuoi condividere la tua esperienza con la community, sei nel posto giusto! 😉

Manda una mail a collaborazioni[at]theredcode.it con la tua proposta e diventa la prossima penna del blog!

Ma sì, facciamolo!