
In Angular tutto ruota attorno ai componenti. Ogni pagina, sezione o elemento visivo che compone un’applicazione è il risultato dell’interazione tra componenti, piccole unità indipendenti che racchiudono logica, struttura e stile. L’approccio a componenti è ciò che rende Angular uno dei framework più potenti e scalabili per la costruzione di interfacce moderne: invece di lavorare su un unico blocco monolitico, si progetta un insieme di parti autonome e riutilizzabili che collaborano tra loro per dare vita a un’esperienza utente coerente e dinamica.
Il motore di tutto questo è il data binding, il meccanismo che mette in comunicazione la logica applicativa scritta in TypeScript con la vista HTML. Attraverso il data binding i dati fluiscono in modo fluido dal componente alla vista e viceversa, consentendo aggiornamenti in tempo reale e un’interazione utente naturale e reattiva.
Nel corso di questo capitolo esploreremo a fondo la struttura e il funzionamento dei componenti, vedremo come il paradigma è evoluto nel tempo passando dai moduli ai componenti standalone, analizzeremo le modalità di data binding e il ruolo delle direttive, fino a comprendere come sfruttare i lifecycle hooks per gestire ogni fase della vita di un componente. Al termine, avrai tutti gli strumenti per costruire interfacce robuste, modulari e semplici da mantenere, sfruttando al massimo le potenzialità di Angular.
Dai moduli ai componenti standalone: l’evoluzione dell’architettura
Per anni l’architettura Angular si è basata sui NgModule, che raggruppavano componenti, direttive, pipe e servizi. Da Angular 14 in poi è stato introdotto un nuovo paradigma: i componenti standalone, pensati per eliminare il boilerplate e semplificare la struttura.
Entrambi gli approcci sono supportati, ma per i nuovi progetti i standalone sono ormai lo standard raccomandato.
Esempio classico con NgModule
Nell’approccio tradizionale, ogni componente deve essere dichiarato all’interno di un modulo:
// user-card.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
templateUrl: './user-card.component.html',
styleUrls: ['./user-card.component.css']
})
export class UserCardComponent {
userName = 'Mario Rossi';
userEmail = 'mario.rossi@example.com';
onCardClick() {
console.log('Card cliccata!');
}
}
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { UserCardComponent } from './user-card/user-card.component';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
UserCardComponent
],
imports: [
BrowserModule,
FormsModule
],
bootstrap: [AppComponent]
})
export class AppModule {}
Questo approccio rimane valido in progetti legacy o in contesti in cui librerie esterne non sono ancora state aggiornate.
Componente standalone: l’approccio moderno
Nel modello moderno, un componente può vivere da solo senza un modulo:
// user-card.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule],
template: `
<div class="user-card">
<h2>{{ userName }}</h2>
<p>Email: {{ userEmail }}</p>
<button (click)="onCardClick()">Contatta</button>
</div>
`,
styles: [`
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
transition: box-shadow 0.3s;
}
.user-card:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
`]
})
export class UserCardComponent {
userName = 'Mario Rossi';
userEmail = 'mario.rossi@example.com';
onCardClick() { console.log('Card cliccata!'); }
}
Il bootstrap dell’app diventa immediato:
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent);
E l’uso del componente figlio è diretto:
// app.component.ts
import { Component } from '@angular/core';
import { UserCardComponent } from './user-card/user-card.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [UserCardComponent],
template: `
<h1>La mia App</h1>
<app-user-card></app-user-card>
`
})
export class AppComponent {}
Perché scegliere i standalone: meno codice ripetitivo, import più chiari e locali, lazy loading più semplice e bundle più piccoli. L’esperienza di sviluppo risulta più fluida e modulare.
Data Binding: connettere logica e interfaccia
Il cuore del dialogo tra componente e template è il data binding, che collega proprietà e metodi della classe al DOM. Angular offre quattro modalità principali, identiche sia con NgModule che con standalone.
-
Interpolazione – Component → Template
-
Property binding – Component → Template
-
Event binding – Template → Component
-
Two-way binding – Comunicazione bidirezionale
Ogni modalità serve a un caso d’uso specifico: mostrare dati, impostare proprietà dinamiche, reagire ad eventi o sincronizzare valori.
Direttive strutturali e attributo
Le direttive strutturali come *ngIf
, *ngFor
e *ngSwitch
modificano il DOM aggiungendo o rimuovendo elementi. Dalla versione 17 sono disponibili anche le nuove sintassi @if
, @for
e @switch
, più leggibili e intuitive, ma perfettamente compatibili con il passato.
Le direttive attributo ([ngClass]
, [ngStyle]
) agiscono invece sull’aspetto e sul comportamento degli elementi senza alterare la struttura del DOM.
Lifecycle Hooks: la vita del componente
Ogni componente attraversa fasi precise dalla creazione alla distruzione. Angular mette a disposizione hook del ciclo di vita per eseguire codice in momenti chiave: dall’inizializzazione (ngOnInit
) alla reazione a cambiamenti (ngOnChanges
), dal rendering (ngAfterViewInit
) alla pulizia finale (ngOnDestroy
).
Capire quando e come usarli è essenziale per gestire correttamente risorse, sottoscrizioni e stato dell’applicazione.
Conclusioni
I componenti sono l’essenza stessa di Angular. Non sono solo “pezzi” dell’interfaccia, ma unità autosufficienti che combinano comportamento, struttura e stile. Con i componenti standalone, questa filosofia raggiunge la sua espressione più semplice e moderna, riducendo complessità e migliorando l’esperienza di sviluppo.
Comprendere il data binding, saper usare le direttive e padroneggiare il ciclo di vita di un componente significa saper costruire applicazioni Angular robuste, scalabili e facili da mantenere — indipendentemente dal fatto che si lavori con l’architettura classica o con quella moderna.