Nelle precedenti lezioni abbiamo illustrato vari esempi in cui è stata utilizzata la direttiva v-model
per associare, in modo bidirezionale (two-way data binding), una proprietà dell’oggetto data
di un componente al valore di un campo di input. Così facendo, ogni volta che una proprietà cambia, viene automaticamente aggiornato il valore del campo di input e viceversa.
Abbiamo visto che per i campi di testo <input type="text">
e <textarea>
al posto della direttiva v-model
potremmo implementare la tecnica di two-way data binding combinando la proprietà value
dell’elemento e l’evento input
come mostrato nel frammento di codice sottostante.
<input
type="text"
@input="value = $event.target.value"
:value="value">
Grazie alla direttiva v-model
possiamo infatti semplificare l’esempio precedente nel seguente modo.
<input
type="text"
v-model="value">
In questa lezione illustreremo come utilizzare la direttiva v-model
con altri elementi di input e descriveremo altre interessanti funzionalità.
Iniziamo allora a presentare degli utili modificatori partendo da .trim
che rimuove automaticamente eventuali spazi bianchi superflui dal valore di un campo di testo.
<input
type="text"
v-model.trim="value">
Ciò significa che se viene inserito il valore ' ciao '
, la proprietà value
conterrà soltanto 'ciao'
dato che tutti gli spazi in eccesso vengono automaticamente eliminati.
Il modificatore .number
si occupa invece di convertire automaticamente in tipo Number
il valore di un campo di input che altrimenti sarebbe di tipo String
. Se il valore non può essere convertito, viene restituito il valore originale.
Consideriamo infatti il seguente frammento di codice.
<input type="number" v-model="value">
Nonostante abbiamo usato l’attributo type="number"
, la proprietà value
viene comunque convertita in una stringa.
Al contrario, grazie al modificatore .number
, la proprietà value
conterrà sempre dei valori di tipo Number
(vale anche per i campi di input con attributo type="text"
).
<input type="number" v-model.number="value">
Se poi vogliamo sincronizzare il valore di un campo con la rispettiva proprietà meno frequentemente, possiamo allora usare il modificatore .lazy
. In questo caso v-model
userà l’evento change
al posto di input
(l’evento change
viene emesso solo al termine della modifica dell’elemento. Per i campi di testo l’evento change
si verifica solo quando l’elemento perde il focus e il suo valore è stato modificato).
<input
type="text"
v-model.lazy="value">
Textarea e v-model
Continuando a parlare di campi di testo, per l’elemento <textarea>
non è possibile utilizzare l’interpolazione, al contrario per settare il suo valore dovremo procedere manualmente attraverso la combinazione di un evento e della proprietà value
. In alternativa possiamo affidarci alla direttiva v-model
come riportato sotto.
<textarea v-model="textValue" placeholder="Testo..."></textarea>
In questo modo ogni volta che si digita un nuovo carattere nella <textarea>
, viene automaticamente aggiornato il valore della proprietà textValue
.
Select
Ma la direttiva v-model
può essere applicata anche ad altri elementi. Illustriamo allora vari esempi partendo dall’elemento <select>
.
<template>
<div>
<select v-model="selected">
<option disabled value="">
Seleziona la libreria/framework preferito
</option>
<option>Angular</option>
<option>React</option>
<option>Vue</option>
</select>
<p>Hai selezionato: {{ selected }}</p>
</div>
</template>
<script>
export default {
data() {
return {
selected: ''
}
}
}
</script>
Con la direttiva v-model
associamo una proprietà di tipo stringa selected
ad una delle opzioni disponibili per l’elemento <select>
. Se per esempio selezioniamo Vue, data.selected
assumerà immediatamente tale valore.
Vue consiglia di utilizzare un elemento <option disabled>
con valore vuoto come opzione predefinita perché, se il valore iniziale dell’espressione associata a v-model
non corrisponde a nessuna delle opzioni presenti, l’elemento <select>
non mostrerà inizialmente nessuna delle opzioni disponibili. Su iOS gli utenti non potranno quindi selezionare alcuna opzione perché in questi casi non viene lanciato l’evento change
.
Grazie alla direttiva v-for
possiamo inoltre costruire i vari elementi <option>
in maniera dinamica.
<template>
<div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option
v-for="option in options"
:key="option"
:value="option">{{ option }}</option>
</select>
<p>Hai selezionato: {{ selected }}</p>
</div>
</template>
<script>
export default {
data() {
return {
selected: '',
options: [
'Angular',
'React',
'Vue'
]
}
}
}
</script>
Sempre attraverso la direttiva v-model
, associata però ad un array, possiamo ottenere le opzioni selezionate in un elemento <select>
con attributo multiple
.
<template>
<div>
<select v-model="selected" multiple>
<option
v-for="option in options"
:key="option"
:value="option">{{ option }}</option>
</select>
<p>Hai selezionato: {{ selected }}</p>
</div>
</template>
<script>
export default {
data() {
return {
selected: [],
options: [
'Angular',
'React',
'Vue'
]
}
}
}
</script>
Checkbox
In modo del tutto simile a quanto visto negli esempi precedenti, possiamo usare la direttiva v-model
con uno o più checkbox. Nel primo caso dovremo associare una singola proprietà, nel secondo ci affideremo ad un array di valori booleani.
<template>
<div>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>
</template>
<script>
export default {
data() {
return {
checked: false
}
}
}
</script>
E come anticipato, in caso di valori multipli utilizzeremo un array.
<template>
<div>
<template v-for="option in options" >
<input
type="checkbox"
:id="option + '-checkbox'"
:value="option"
v-model="checked"
:key="option">
<label :for="option + '-checkbox'" :key="option + '-label'">
{{ option.charAt(0).toUpperCase() + option.slice(1) }}
</label>
</template>
<div>
Hai selezionato:<br> {{ checked }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
checked: [],
options: [
'angular',
'react',
'vue'
]
}
}
}
</script>
Pulsanti Radio
Per i pulsanti di tipo radio possiamo invece utilizzare la direttiva v-model
come mostrato nel seguente esempio.
<template>
<div>
<template v-for="option in options" >
<input
type="radio"
:id="option + '-checkbox'"
:value="option"
v-model="selected"
:key="option">
<label :for="option + '-checkbox'" :key="option + '-label'">
{{ option.charAt(0).toUpperCase() + option.slice(1) }}
</label>
</template>
<div>
Hai selezionato:<br> {{ selected }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
selected: 'vue',
options: [
'angular',
'react',
'vue'
]
}
}
}
</script>
Nell’esempio riportato sopra, i diversi pulsanti di tipo radio sono collegati tutti alla stessa proprietà selected
che è inizializzata al valore ‘vue’. In questo modo il pulsante radio con value
pari a ‘vue’ sarà quello predefinito. Ogni volta che selezioniamo un diverso pulsante, la proprietà selected
assumerà di volta in volta un nuovo valore.
Esempio di form realizzato in Vue
Per concludere questa lezione vediamo come combinare quanto illustrato finora e realizzare un semplice form con diversi campi di input.
<template>
<form @submit.prevent="onSubmit">
<label for="name">Nome</label>
<input type="text" id="name" v-model.trim="details.name">
<label for="email">E-mail</label>
<input type="email" id="email" v-model.trim="details.email">
<label for="age">Età</label>
<input type="number" id="age" v-model.number="details.age">
<select v-model="details.favoriteJsFramework">
<option disabled value="">Seleziona JS framework preferito</option>
<option
v-for="jsFramework in jsFrameworksList"
:key="jsFramework"
:value="jsFramework">
{{ jsFramework }}
</option>
</select>
<fieldset>
<legend>Framework CSS conosciuti</legend>
<template v-for="cssFramework in cssFrameworksList">
<input
:key="cssFramework"
type="checkbox"
:id="cssFramework"
:value="cssFramework"
v-model="details.cssFrameworks">
<label
:key="cssFramework + '-label'"
:for="cssFramework">{{ cssFramework }}</label>
</template>
</fieldset>
<fieldset>
<legend>Javascript è il tuo linguaggio preferito?</legend>
<!-- grazie a v-bind associamo un valore booleano -->
<!-- a ciascun pulsante di tipo radio -->
<input
type="radio"
id="yes"
:value="true"
v-model="details.isJSFavoriteLanguage">
<label for="yes">Sì</label>
<input
type="radio"
id="no"
:value="false"
v-model="details.isJSFavoriteLanguage">
<label for="no">No</label>
</fieldset>
<button type="submit">Invia</button>
</form>
</template>
<script>
export default {
data() {
return {
details: {
name: '',
email: '',
age: 0,
favoriteJsFramework: '',
cssFrameworks: [],
isJSFavoriteLanguage: true
},
jsFrameworksList: [
'Angular',
'React',
'Vue'
],
cssFrameworksList: [
'Bootstrap',
'Foundation',
'Pure',
'Semantic UI',
'Tailwind'
]
}
},
methods: {
onSubmit() {
console.log(this.details);
}
}
}
</script>
Nel frammento di codice riportato sopra intercettiamo l’evento submit
sul form e tramite modificatore .prevent
ci assicuriamo che non venga ricaricata la pagina. Nel metodo onSubmit()
ci limitiamo a stampare nella console del browser l’oggetto this.$data.details
.
Bisogna precisare che nell’esempio appena visto abbiamo però trascurato qualsiasi forma di gestione degli errori e di validazione dei campi che è tuttavia possibile implementare grazie alle tecniche, ai concetti e agli strumenti presentati nelle precedenti lezioni. Per semplicità non abbiamo neanche utilizzato alcuna regola CSS per definire lo stile del form.
Riepilogo
In questa lezione abbiamo illustrato come creare dei form in Vue.js e abbiamo visto come utilizzare la direttiva v-model
con i diversi tipi di elementi <input>
. Nella prossima lezione vedremo come definire ed usare delle direttive personalizzate.