00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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 + Change per l’output
  • Mantieni i template leggibili: se il template diventa troppo complesso, suddividi in componenti figli
  • Usa trackBy (o track nella nuova sintassi) nelle liste per ottimizzare il rendering