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

Sintassi dei Template

Interpolazione di testo

Vue utilizza la sintassi a doppia parentesi graffa (mustache) per il data binding nel template. L’espressione viene valutata e il risultato viene inserito come testo nel DOM.

<script setup>
import { ref } from 'vue'

const messaggio = ref('Ciao mondo!')
const contatore = ref(42)
const utente = ref({ nome: 'Mario', cognome: 'Rossi' })
</script>

<template>
  <!-- Testo semplice -->
  <p>{{ messaggio }}</p>

  <!-- Espressioni JavaScript -->
  <p>{{ contatore + 1 }}</p>
  <p>{{ messaggio.split('').reverse().join('') }}</p>

  <!-- Operatore ternario -->
  <p>{{ contatore > 40 ? 'Grande' : 'Piccolo' }}</p>

  <!-- Accesso a proprieta di oggetti -->
  <p>{{ utente.nome }} {{ utente.cognome }}</p>

  <!-- Chiamata a metodi -->
  <p>{{ messaggio.toUpperCase() }}</p>
</template>

Limitazioni delle espressioni

Le interpolazioni supportano solo espressioni singole, non dichiarazioni o flussi di controllo.

<template>
  <!-- Valido: espressione -->
  <p>{{ ok ? 'Si' : 'No' }}</p>

  <!-- NON valido: dichiarazione -->
  <!-- <p>{{ let x = 1 }}</p> -->

  <!-- NON valido: flusso di controllo -->
  <!-- <p>{{ if (ok) { return 'Si' } }}</p> -->
</template>

v-bind: binding degli attributi

La direttiva v-bind lega dinamicamente un attributo HTML a un’espressione JavaScript. La sintassi abbreviata usa :.

<script setup>
import { ref } from 'vue'

const urlImmagine = ref('/img/logo.png')
const altTesto = ref('Logo del sito')
const idElemento = ref('contenitore-principale')
const isDisabilitato = ref(true)
const classeAttiva = ref('attivo')
</script>

<template>
  <!-- Sintassi completa -->
  <img v-bind:src="urlImmagine" v-bind:alt="altTesto" />

  <!-- Sintassi abbreviata (consigliata) -->
  <img :src="urlImmagine" :alt="altTesto" />

  <!-- Binding dinamico dell'id -->
  <div :id="idElemento">Contenuto</div>

  <!-- Attributi booleani -->
  <button :disabled="isDisabilitato">Invia</button>

  <!-- Binding di classe -->
  <div :class="classeAttiva">Elemento</div>
</template>

Binding di classi

Vue offre una sintassi avanzata per la gestione delle classi CSS.

<script setup>
import { ref } from 'vue'

const isAttivo = ref(true)
const hasErrore = ref(false)
const tipo = ref('primario')
</script>

<template>
  <!-- Oggetto: chiave = classe, valore = condizione -->
  <div :class="{ attivo: isAttivo, errore: hasErrore }">
    Classi condizionali
  </div>

  <!-- Array di classi -->
  <div :class="[tipo, { attivo: isAttivo }]">
    Mix array e oggetto
  </div>

  <!-- Con classe statica -->
  <div class="base" :class="{ attivo: isAttivo }">
    Classe statica + dinamica
  </div>
</template>

Binding di stili inline

<script setup>
import { ref, computed } from 'vue'

const colore = ref('#42b883')
const dimensione = ref(18)

const stileComputed = computed(() => ({
  color: colore.value,
  fontSize: `${dimensione.value}px`,
  fontWeight: 'bold'
}))
</script>

<template>
  <!-- Oggetto stile -->
  <p :style="{ color: colore, fontSize: dimensione + 'px' }">
    Testo colorato
  </p>

  <!-- Stile computed -->
  <p :style="stileComputed">Testo con stile computed</p>

  <!-- Array di stili -->
  <p :style="[stileComputed, { textDecoration: 'underline' }]">
    Stili multipli
  </p>
</template>

v-on: gestione eventi

La direttiva v-on ascolta eventi DOM e esegue codice quando vengono emessi. La sintassi abbreviata usa @.

<script setup>
import { ref } from 'vue'

const contatore = ref(0)
const nome = ref('')

function saluta(evento: Event) {
  alert(`Ciao! Evento: ${evento.type}`)
}

function incrementaDi(n: number) {
  contatore.value += n
}
</script>

<template>
  <!-- Espressione inline -->
  <button @click="contatore++">+1</button>

  <!-- Metodo handler -->
  <button @click="saluta">Saluta</button>

  <!-- Metodo con argomenti -->
  <button @click="incrementaDi(5)">+5</button>

  <!-- Accesso all'evento nativo con $event -->
  <input @input="nome = ($event.target as HTMLInputElement).value" />

  <!-- Eventi multipli -->
  <button
    @mouseenter="contatore++"
    @mouseleave="contatore--"
  >
    Hover: {{ contatore }}
  </button>
</template>

Modificatori di eventi

I modificatori sono suffissi speciali che modificano il comportamento degli event handler.

<template>
  <!-- Previeni il comportamento predefinito -->
  <form @submit.prevent="invia">
    <button type="submit">Invia</button>
  </form>

  <!-- Stoppa la propagazione -->
  <div @click="esterno">
    <button @click.stop="interno">Non propaga</button>
  </div>

  <!-- Esegui solo una volta -->
  <button @click.once="inizializza">Solo una volta</button>

  <!-- Solo se il target e l'elemento stesso -->
  <div @click.self="soloSeSelf">
    <button>Click su button non attiva il div</button>
  </div>

  <!-- Cattura nella fase di capturing -->
  <div @click.capture="cattura">Capturing</div>

  <!-- Modificatori della tastiera -->
  <input @keyup.enter="invia" />
  <input @keyup.esc="annulla" />
  <input @keyup.tab="prossimoCampo" />

  <!-- Combinazioni di tasti -->
  <input @keyup.ctrl.enter="inviaConCtrl" />
  <input @keyup.alt.s="salva" />

  <!-- Modificatori del mouse -->
  <button @click.left="clickSinistro">Solo sinistro</button>
  <button @click.right.prevent="menuContestuale">Destro</button>
  <button @click.middle="clickCentrale">Centrale</button>
</template>

v-model: binding bidirezionale

v-model crea un binding bidirezionale tra un input e una variabile reattiva.

<script setup>
import { ref } from 'vue'

const testo = ref('')
const numero = ref(0)
const accettato = ref(false)
const coloreSelezionato = ref('rosso')
const linguaggi = ref<string[]>([])
const commento = ref('')
</script>

<template>
  <!-- Input testo -->
  <input v-model="testo" placeholder="Scrivi qualcosa" />

  <!-- Textarea -->
  <textarea v-model="commento" rows="3"></textarea>

  <!-- Checkbox singolo (booleano) -->
  <label>
    <input type="checkbox" v-model="accettato" />
    Accetto i termini
  </label>

  <!-- Checkbox multipli (array) -->
  <label>
    <input type="checkbox" v-model="linguaggi" value="javascript" />
    JavaScript
  </label>
  <label>
    <input type="checkbox" v-model="linguaggi" value="typescript" />
    TypeScript
  </label>

  <!-- Radio -->
  <label>
    <input type="radio" v-model="coloreSelezionato" value="rosso" />
    Rosso
  </label>
  <label>
    <input type="radio" v-model="coloreSelezionato" value="verde" />
    Verde
  </label>

  <!-- Select -->
  <select v-model="coloreSelezionato">
    <option value="rosso">Rosso</option>
    <option value="verde">Verde</option>
    <option value="blu">Blu</option>
  </select>
</template>

Modificatori di v-model

<template>
  <!-- .lazy: aggiorna su "change" invece di "input" -->
  <input v-model.lazy="testo" />

  <!-- .number: converte automaticamente a numero -->
  <input v-model.number="eta" type="number" />

  <!-- .trim: rimuove spazi iniziali e finali -->
  <input v-model.trim="nome" />
</template>

Rendering condizionale

v-if, v-else-if, v-else

Queste direttive aggiungono o rimuovono elementi dal DOM in base a una condizione.

<script setup>
import { ref } from 'vue'

const punteggio = ref(85)
const isAutenticato = ref(true)
const ruolo = ref('admin')
</script>

<template>
  <!-- Condizione semplice -->
  <p v-if="isAutenticato">Benvenuto!</p>
  <p v-else>Effettua il login</p>

  <!-- Catena condizionale -->
  <div v-if="punteggio >= 90">Eccellente!</div>
  <div v-else-if="punteggio >= 70">Buono</div>
  <div v-else-if="punteggio >= 50">Sufficiente</div>
  <div v-else>Insufficiente</div>

  <!-- Condizionale su gruppo con template -->
  <template v-if="ruolo === 'admin'">
    <h2>Pannello Admin</h2>
    <p>Hai accesso completo</p>
    <button>Gestisci utenti</button>
  </template>
</template>

v-show

v-show usa display: none per nascondere l’elemento senza rimuoverlo dal DOM. Preferibile quando si alterna frequentemente la visibilita.

<script setup>
import { ref } from 'vue'

const mostraDettagli = ref(false)
</script>

<template>
  <button @click="mostraDettagli = !mostraDettagli">
    {{ mostraDettagli ? 'Nascondi' : 'Mostra' }} dettagli
  </button>

  <!-- v-show: sempre nel DOM, toggle CSS display -->
  <div v-show="mostraDettagli">
    <p>Dettagli che si mostrano/nascondono frequentemente</p>
  </div>
</template>

Quando usare v-if vs v-show

Caratteristica v-if v-show
Rendering Lazy (solo quando true) Sempre renderizzato
Costo toggle Alto (crea/distrugge DOM) Basso (cambia CSS)
Costo iniziale Basso se false Alto (sempre renderizzato)
Usa quando Condizione cambia raramente Toggle frequente

Rendering di liste con v-for

<script setup>
import { ref } from 'vue'

const frutta = ref(['Mela', 'Banana', 'Arancia', 'Kiwi'])
const utenti = ref([
  { id: 1, nome: 'Mario', attivo: true },
  { id: 2, nome: 'Luigi', attivo: false },
  { id: 3, nome: 'Peach', attivo: true }
])
const configurazione = ref({ tema: 'scuro', lingua: 'it', notifiche: true })
</script>

<template>
  <!-- Array semplice con indice -->
  <ul>
    <li v-for="(frutto, indice) in frutta" :key="indice">
      {{ indice + 1 }}. {{ frutto }}
    </li>
  </ul>

  <!-- Array di oggetti (usa sempre :key con id unico) -->
  <div v-for="utente in utenti" :key="utente.id">
    <span :class="{ attivo: utente.attivo }">{{ utente.nome }}</span>
  </div>

  <!-- Iterazione su oggetto -->
  <div v-for="(valore, chiave) in configurazione" :key="chiave">
    {{ chiave }}: {{ valore }}
  </div>

  <!-- Range numerico -->
  <span v-for="n in 5" :key="n">{{ n }} </span>

  <!-- v-for con template per gruppi -->
  <template v-for="utente in utenti" :key="utente.id">
    <h3>{{ utente.nome }}</h3>
    <p>Stato: {{ utente.attivo ? 'Attivo' : 'Inattivo' }}</p>
    <hr />
  </template>
</template>

v-for con v-if

Non usare v-if sullo stesso elemento di v-for. Usa un <template> wrapper o una computed property.

<script setup>
import { ref, computed } from 'vue'

const utenti = ref([
  { id: 1, nome: 'Mario', attivo: true },
  { id: 2, nome: 'Luigi', attivo: false },
  { id: 3, nome: 'Peach', attivo: true }
])

// Approccio corretto: computed che filtra
const utentiAttivi = computed(() =>
  utenti.value.filter(u => u.attivo)
)
</script>

<template>
  <!-- Corretto: usa computed filtrata -->
  <li v-for="utente in utentiAttivi" :key="utente.id">
    {{ utente.nome }}
  </li>

  <!-- Alternativa: template wrapper -->
  <template v-for="utente in utenti" :key="utente.id">
    <li v-if="utente.attivo">{{ utente.nome }}</li>
  </template>
</template>

Binding dinamico di attributi multipli

<script setup>
const attributiInput = {
  id: 'campo-email',
  type: 'email',
  placeholder: 'Inserisci email',
  required: true,
  class: 'input-campo'
}
</script>

<template>
  <!-- Spread di tutti gli attributi -->
  <input v-bind="attributiInput" />
</template>

Best practice

  • Usa sempre :key unico con v-for, preferibilmente un ID e non l’indice
  • Preferisci la sintassi abbreviata (: per v-bind, @ per v-on)
  • Non mischiare v-for e v-if sullo stesso elemento
  • Usa v-show per toggle frequenti e v-if per condizioni che cambiano raramente
  • Mantieni le espressioni nel template semplici e usa computed per logica complessa
  • Usa <template> come wrapper invisibile per raggruppare elementi con direttive