Sintassi dei Template
Interpolazione
L’interpolazione permette di inserire valori dinamici nel template utilizzando la sintassi con le doppie parentesi graffe {{ }}. Angular valuta l’espressione all’interno e la converte in stringa.
import { Component } from '@angular/core';
@Component({
selector: 'app-profilo',
standalone: true,
template: `
<h1>{{ titolo }}</h1>
<p>Nome completo: {{ nome }} {{ cognome }}</p>
<p>Età: {{ eta }}</p>
<!-- Espressioni -->
<p>Anno di nascita: {{ 2026 - eta }}</p>
<p>Nome maiuscolo: {{ nome.toUpperCase() }}</p>
<p>Ha più di 18 anni: {{ eta >= 18 ? 'Sì' : 'No' }}</p>
<!-- Chiamata a metodi -->
<p>Saluto: {{ getSaluto() }}</p>
`
})
export class ProfiloComponent {
titolo = 'Il Mio Profilo';
nome = 'Marco';
cognome = 'Rossi';
eta = 28;
getSaluto(): string {
return `Ciao, sono ${this.nome} ${this.cognome}!`;
}
}
Cosa si puo usare nell’interpolazione
L’interpolazione supporta espressioni JavaScript semplici, ma con alcune limitazioni:
<!-- Consentito -->
{{ proprietà }}
{{ oggetto.proprietà }}
{{ array[0] }}
{{ metodo() }}
{{ condizione ? 'vero' : 'falso' }}
{{ valore1 + valore2 }}
{{ stringa.length }}
<!-- NON consentito -->
{{ let x = 5 }} <!-- assegnazioni -->
{{ new Date() }} <!-- operatore new -->
{{ console.log('x') }} <!-- effetti collaterali -->
{{ a; b }} <!-- espressioni multiple -->
Property Binding
Il property binding permette di impostare il valore di una proprietà di un elemento HTML o di un componente Angular. Si usa la sintassi con le parentesi quadre [proprietà].
@Component({
selector: 'app-immagine',
standalone: true,
template: `
<!-- Binding a proprietà HTML -->
<img [src]="urlImmagine" [alt]="descrizione">
<button [disabled]="isDisabilitato">Invia</button>
<div [hidden]="isNascosto">Contenuto nascosto</div>
<!-- Binding a proprietà di input di componenti -->
<app-card [titolo]="titoloProdotto" [prezzo]="prezzoProdotto" />
<!-- Binding a attributi HTML con attr. -->
<td [attr.colspan]="colonne">Cella larga</td>
<div [attr.role]="ruolo" [attr.aria-label]="etichetta">
Contenuto accessibile
</div>
<!-- Binding a classi CSS -->
<div [class.attivo]="isAttivo">Elemento</div>
<div [class.errore]="haErrore">Messaggio</div>
<!-- Binding a stili inline -->
<div [style.color]="colore">Testo colorato</div>
<div [style.font-size.px]="dimensioneFont">Testo grande</div>
<div [style.width.%]="larghezza">Barra</div>
`
})
export class ImmagineComponent {
urlImmagine = 'assets/immagini/foto.jpg';
descrizione = 'Una bellissima foto';
isDisabilitato = false;
isNascosto = false;
titoloProdotto = 'Laptop Pro';
prezzoProdotto = 1299;
colonne = 3;
ruolo = 'button';
etichetta = 'Chiudi finestra';
isAttivo = true;
haErrore = false;
colore = '#3b82f6';
dimensioneFont = 18;
larghezza = 75;
}
Interpolazione vs Property Binding
<!-- Queste due forme sono equivalenti per le stringhe -->
<img src="{{ urlImmagine }}">
<img [src]="urlImmagine">
<!-- Ma per valori non-stringa, DEVI usare il property binding -->
<button [disabled]="isDisabilitato">OK</button>
<!-- Questo NON funziona come previsto: -->
<button disabled="{{ isDisabilitato }}">OK</button>
<!-- Imposta disabled="true" (stringa), che è sempre truthy -->
Event Binding
L’event binding permette di ascoltare eventi del DOM e rispondere con metodi del componente. Si usa la sintassi con le parentesi tonde (evento).
@Component({
selector: 'app-form-contatto',
standalone: true,
template: `
<!-- Click -->
<button (click)="onClick()">Clicca qui</button>
<!-- Click con oggetto evento -->
<button (click)="onClickConEvento($event)">Dettagli Click</button>
<!-- Eventi del mouse -->
<div
(mouseenter)="onMouseEnter()"
(mouseleave)="onMouseLeave()"
[class.evidenziato]="isEvidenziato"
>
Passa il mouse qui
</div>
<!-- Eventi della tastiera -->
<input
(keyup)="onKeyUp($event)"
(keyup.enter)="onInvio()"
(keyup.escape)="onEsc()"
placeholder="Scrivi qualcosa..."
>
<!-- Eventi di input -->
<input
(input)="onInput($event)"
(focus)="onFocus()"
(blur)="onBlur()"
>
<!-- Evento submit del form -->
<form (submit)="onSubmit($event)">
<button type="submit">Invia</button>
</form>
<p>{{ messaggio }}</p>
`
})
export class FormContattoComponent {
messaggio = '';
isEvidenziato = false;
onClick(): void {
this.messaggio = 'Bottone cliccato!';
}
onClickConEvento(event: MouseEvent): void {
this.messaggio = `Click alle coordinate: ${event.clientX}, ${event.clientY}`;
}
onMouseEnter(): void {
this.isEvidenziato = true;
}
onMouseLeave(): void {
this.isEvidenziato = false;
}
onKeyUp(event: KeyboardEvent): void {
this.messaggio = `Tasto premuto: ${event.key}`;
}
onInvio(): void {
this.messaggio = 'Hai premuto Invio!';
}
onEsc(): void {
this.messaggio = 'Hai premuto Escape!';
}
onInput(event: Event): void {
const target = event.target as HTMLInputElement;
this.messaggio = `Valore: ${target.value}`;
}
onFocus(): void {
console.log('Campo in focus');
}
onBlur(): void {
console.log('Campo ha perso il focus');
}
onSubmit(event: Event): void {
event.preventDefault();
this.messaggio = 'Form inviato!';
}
}
Filtri sugli eventi tastiera
Angular supporta filtri predefiniti per gli eventi della tastiera:
<!-- Tasti specifici -->
<input (keyup.enter)="cerca()">
<input (keyup.escape)="annulla()">
<input (keydown.tab)="prossimoCampo()">
<input (keyup.space)="toggleSelezione()">
<!-- Combinazioni di tasti -->
<input (keydown.control.s)="salva()">
<input (keydown.shift.enter)="inviaERimani()">
<input (keydown.alt.a)="selezionaTutto()">
Two-Way Binding con ngModel
Il two-way binding combina property binding e event binding, permettendo di sincronizzare i dati tra il componente e il template in entrambe le direzioni. Si usa la sintassi [(ngModel)], nota come “banana in a box”.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-form-ricerca',
standalone: true,
imports: [FormsModule],
template: `
<h2>Ricerca Prodotti</h2>
<!-- Two-way binding con ngModel -->
<input [(ngModel)]="termineRicerca" placeholder="Cerca...">
<p>Stai cercando: {{ termineRicerca }}</p>
<!-- Select con ngModel -->
<select [(ngModel)]="categoriaSelezionata">
<option value="">Tutte le categorie</option>
@for (cat of categorie; track cat) {
<option [value]="cat">{{ cat }}</option>
}
</select>
<p>Categoria: {{ categoriaSelezionata }}</p>
<!-- Checkbox con ngModel -->
<label>
<input type="checkbox" [(ngModel)]="soloDisponibili">
Mostra solo disponibili
</label>
<p>Solo disponibili: {{ soloDisponibili }}</p>
<!-- Range con ngModel -->
<input type="range" [(ngModel)]="prezzoMax" min="0" max="1000">
<p>Prezzo massimo: {{ prezzoMax }}€</p>
`
})
export class FormRicercaComponent {
termineRicerca = '';
categoriaSelezionata = '';
soloDisponibili = false;
prezzoMax = 500;
categorie = ['Elettronica', 'Abbigliamento', 'Casa', 'Sport'];
}
Come funziona il two-way binding
La sintassi [(ngModel)] è in realta un’abbreviazione che combina property binding e event binding:
<!-- Questa forma abbreviata -->
<input [(ngModel)]="nome">
<!-- È equivalente a questa forma estesa -->
<input [ngModel]="nome" (ngModelChange)="nome = $event">
Two-way binding su componenti custom
Puoi creare il tuo two-way binding personalizzato:
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-slider',
standalone: true,
template: `
<input
type="range"
[value]="valore"
(input)="onInput($event)"
[min]="min"
[max]="max"
>
<span>{{ valore }}</span>
`
})
export class SliderComponent {
@Input() valore = 0;
@Input() min = 0;
@Input() max = 100;
// Il nome DEVE essere: nomeInput + 'Change'
@Output() valoreChange = new EventEmitter<number>();
onInput(event: Event): void {
const nuovoValore = +(event.target as HTMLInputElement).value;
this.valoreChange.emit(nuovoValore);
}
}
<!-- Ora puoi usare il two-way binding -->
<app-slider [(valore)]="luminosità" [min]="0" [max]="100" />
<p>Luminosità: {{ luminosità }}%</p>
Template Reference Variables
Le template reference variables (#variabile) permettono di ottenere un riferimento a un elemento del DOM o a un componente direttamente nel template.
@Component({
selector: 'app-form-login',
standalone: true,
imports: [FormsModule],
template: `
<!-- Riferimento a un elemento HTML -->
<input #campoEmail type="email" placeholder="Email">
<button (click)="mostraValore(campoEmail.value)">
Mostra Email
</button>
<!-- Riferimento a un componente -->
<app-timer #timer />
<button (click)="timer.start()">Avvia Timer</button>
<button (click)="timer.stop()">Ferma Timer</button>
<!-- Riferimento a una direttiva (ngForm) -->
<form #mioForm="ngForm" (ngSubmit)="onSubmit(mioForm)">
<input
name="nome"
ngModel
required
#campoNome="ngModel"
>
@if (campoNome.invalid && campoNome.touched) {
<span class="errore">Il nome è obbligatorio</span>
}
<button
type="submit"
[disabled]="mioForm.invalid"
>
Invia
</button>
</form>
`
})
export class FormLoginComponent {
mostraValore(valore: string): void {
console.log('Valore:', valore);
}
onSubmit(form: any): void {
console.log('Dati form:', form.value);
}
}
Nuova Sintassi di Controllo del Flusso (Angular 17+)
Angular 17 ha introdotto una nuova sintassi di controllo del flusso che sostituisce le direttive strutturali.
@Component({
selector: 'app-lista',
standalone: true,
template: `
<!-- @if al posto di *ngIf -->
@if (utente) {
<p>Benvenuto, {{ utente.nome }}!</p>
} @else if (caricamento) {
<p>Caricamento in corso...</p>
} @else {
<p>Effettua il login.</p>
}
<!-- @for al posto di *ngFor -->
@for (prodotto of prodotti; track prodotto.id) {
<div class="prodotto">
<h3>{{ prodotto.nome }}</h3>
<p>{{ prodotto.prezzo | currency:'EUR' }}</p>
</div>
} @empty {
<p>Nessun prodotto trovato.</p>
}
<!-- @switch al posto di *ngSwitch -->
@switch (stato) {
@case ('attivo') {
<span class="badge verde">Attivo</span>
}
@case ('in_pausa') {
<span class="badge giallo">In Pausa</span>
}
@case ('disattivato') {
<span class="badge rosso">Disattivato</span>
}
@default {
<span class="badge grigio">Sconosciuto</span>
}
}
`
})
export class ListaComponent {
utente: { nome: string } | null = { nome: 'Anna' };
caricamento = false;
prodotti = [
{ id: 1, nome: 'Tastiera', prezzo: 79.99 },
{ id: 2, nome: 'Mouse', prezzo: 49.99 }
];
stato = 'attivo';
}
Best Practices
- Usa l’interpolazione solo per stringhe; usa il property binding per valori booleani e oggetti
- Evita di chiamare metodi complessi nel template: possono causare problemi di performance con il change detection
- Preferisci la nuova sintassi di controllo del flusso (
@if,@for,@switch) nelle versioni Angular 17+ - Usa le template reference variables per accedere a elementi del DOM senza manipolazione diretta
- Nel two-way binding personalizzato, segui la convenzione
nomeInput+Changeper l’output - Mantieni i template leggibili: se il template diventa troppo complesso, suddividi in componenti figli
- Usa
trackBy(otracknella nuova sintassi) nelle liste per ottimizzare il rendering