Vue, vuetify: How to validate input immediately after creating, adding it - vue.js

I'm trying to figure out how to validate inputs immediately after adding them:
template:
<div id="app">
<div
v-for="(message, index) in messages"
:key="index"
>
<v-text-field
v-model="messages[index]"
:rules="[v => !!v || 'Error: Please enter text']"
/>
</div>
<v-btn #click="add()">Add input</v-btn>
</div>
JS:
new Vue({
el: '#app',
data: {
messages: ['Hi', 'Hello'],
},
methods: {
add(val) {
this.messages.push(val);
},
},
});
Codepen: https://codepen.io/Zurab-D/pen/dymGaRY
What I mean is that when the button is clicked, a new input appears and there is no "Error: Please enter text" error message by default.
Can I make this message appear immediately?

You can put your template's code, where you use v-for directive, between v-form tags:
<v-form ref="form">
<div v-for="(message, index) in messages" :key="index">
<v-text-field v-model="messages[index]"
:rules="[v => !!v || 'Error: Please enter text']" />
</div>
<v-btn #click="add()">Add input</v-btn>
</v-form>
and then inside add() method validate form by using built-in validate() function in v-form reference
async add(val) {
this.messages.push(val);
await this.$nextTick(); // wait until a new text-field will be rendered
this.$refs.form.validate(); //validate form
},
An example on CodePen is here

To achieve this you can use this.$refs.form.validate(). Wrap your inputs inside <v-form> tag and add an attribute ref="form".
Live Demo :
new Vue({
vuetify: new Vuetify(),
data: {
messages: ['Hi', 'Hello']
},
methods: {
required: value => !!value || 'Please enter text',
add(val) {
this.messages.push(val);
setTimeout(() => {
this.$refs.form.validate();
})
}
}
}).$mount('#app')
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css"/>
<div id="app">
<v-app>
<v-form ref="form">
<div v-for="(message, index) in messages" :key="index">
<v-text-field v-model="messages[index]" :rules="[required]"/>
</div>
<v-btn #click="add()">Add input</v-btn>
</v-form>
</v-app>
</div>

Related

How to search charged option in a v-select

i have this v-select whose items charge dinamicaly, the user can choose several options from it, the problem is i have to many options and i want the user can search the option that he/she wants writing text:
<v-col cols="7">
<v-select
v-model="fillModReparacion.listamanodeObraC"
:items="fillModReparacion.listamanodeObra"
item-value="itg_id"
item-text="itg_descripcion"
attach
chips
label="Mano de Obra"
multiple
outlined
clearable
return-object
></v-select>
</v-col>
You can use v-slot:prepend-item to prepend the search before the options list inside v-select.
Live Demo :
new Vue({
el: "#app",
vuetify: new Vuetify(),
data: () => ({
searchTerm: "",
fruits: [
"Apples",
"Apricots",
"Avocado",
"Bananas",
"Blueberries",
"Blackberries",
"Dates",
"Eggplant",
"Figs",
"Grapes",
"Grapefruit",
"Guava"
],
fruitsCopy: [],
selectedFruits: []
}),
mounted() {
this.fruitsCopy = [...this.fruits];
},
computed: {},
methods: {
searchFruits(e) {
if (!this.searchTerm) {
this.fruits = this.fruitsCopy;
}
this.fruits = this.fruitsCopy.filter((fruit) => {
return fruit.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
});
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.2.21/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify#2.2.21/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css"/>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-select v-model="selectedFruits" :items="fruits" attach label="Favorite Fruits" multiple>
<template v-slot:prepend-item>
<v-list-item>
<v-list-item-content>
<v-text-field v-model="searchTerm" placeholder="Search" #input="searchFruits"></v-text-field>
</v-list-item-content>
</v-list-item>
<v-divider class="mt-2"></v-divider>
</template>
</v-select>
</v-container>
</v-app>
</div>

Add animation to append-icon in v-text-field

When a new account is registered, I display login and password details on the screen. The user can copy login&passwod details to clipboard and I would like to run animation when the append-icon (scale up, increase font or replace with another icon) is clicked. What would be the right way to do that?
<template>
<v-card class="col-12 col-md-8 col-lg-6 p-6 px-16" elevation="4">
<div class="title h2 mb-10 text-uppercase text-center">
Success
<v-icon color="green" x-large>
mdi-check-circle
</v-icon>
</div>
<v-text-field
:value="newAccount.login"
label="Login"
outlined
readonly
append-icon="mdi-content-copy"
ref='login'
#click:append="copy('login')"
></v-text-field>
<v-text-field
:value="newAccount.password"
label="Password"
outlined
readonly
append-icon="mdi-content-copy"
ref='login'
#click:append="copy('login')"
></v-text-field>
</v-card>
</template>
<script>
methods: {
copy(field) {
const input = this.$refs[field].$refs.input
input.select()
document.execCommand('copy')
input.setSelectionRange(0,0)
// apply append-icon animation
}
}
</script>
There is multiple answers to this, but to answer one of them: "replacing icon" would be pretty straight forward.
You would have to change the append-icon prop do be dynamic :, then assign a variable to it like copyIcon. You will then update this variable depending on the state of the copy.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
copyIcon: 'mdi-content-copy',
newAccount: {
login: 'GhostDestroyer69',
password: '',
},
},
methods: {
copy(field) {
const input = this.$refs[field].$refs.input
input.select()
document.execCommand('copy')
input.setSelectionRange(0, 0)
// Set icon to check
this.copyIcon = 'mdi-check'
// Reset icon to copy after 1 second
setTimeout(() => {
this.copyIcon = 'mdi-content-copy'
}, 1000)
}
}
})
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-card class="col-12 col-md-8 col-lg-6 p-6 px-16" elevation="4">
<v-text-field
:value="newAccount.login"
label="Login"
outlined
readonly
:append-icon="copyIcon"
ref='login'
#click:append="copy('login')"
></v-text-field>
</v-card>
</div>
mdi has animation helpers like
append-icon="mdi-content-copy mdi-spin"
For example.

validateAll doesn't work with inputs generated by v-for

I've got a form in which the inputs are added dynamically with the v-for loop. Each field should be validated, and before user submit the form it should be checked wherever it's valid or not. The problem is that the this.$validator.validateAll() always return true, even if the inputs are invalid. What I'm doing wrong?
<div id="app">
<v-app id="inspire">
<v-flex md4 offset-md4>
<form data-vv-scope="photoForm">
<v-text-field v-for="index in 5"
:key="index"
:label="'photo' + index"
:error-messages="errors.collect('photoForm.photoName' + index)"
v-validate="'max:10'"
:data-vv-name="'photoForm.photoName' + index"
color="purple" autocomplete="on"
counter="10" >
</v-text-field>
</form>
<p>Is valid form? {{ validationResult }}</p>
</v-flex>
<v-btn #click="validate" color="purple" dark>
validate
</v-btn>
</v-app>
</div>
Vue.use(VeeValidate);
new Vue({
el: "#app",
data() {
return {
validationResult: ''
}
},
methods: {
validate() {
this.$validator.validateAll('photoForm').then(result => {
this.validationResult = result
})
}
}
});
And codepen where I reproduce the problem: https://codepen.io/anon/pen/jjrJdE
You need to store your form data somewhere so the validation has something to work on, I assume.
See https://codepen.io/cwg999/pen/MMjWNj?editors=1011
The main changes I made were to put your dynamically generated inputs into your data() and used that to reference them in the for-loop.
(note: you can also use v-model instead of :value/#input)
<v-text-field v-for="o,i in photoForm"
:key="i"
:label="o.label+ ' ' + (i+1)"
:error-messages="errors.collect('photoForm.photoName' + i)"
v-validate="'max:10'"
:name="'photoName' + i"
:value=o.value
#input="o.value = $event"
color="purple" autocomplete="on"
counter="10" >
</v-text-field>
data() {
return {
validationResult: '',
photoForm:[
{label:'Photo',value:''},
{label:'Photo',value:''}
]
}
},

Adding a entry in a v-select in vuetify

I want to add the items in a v-select. for example, we dont have any item in a v-select and want to add it extrenaly.
<v-select
v-bind:label="Intelligence"
v-model="IntValues"
multiple >
</v-select>
When we write like this we will get only empty select list but how to add items into it externally
Here IntValues:[],
Or Editable list, like TodoMVC
new Vue({
el: '#app',
data() {
return {
selected: null,
items: []
}
},
methods: {
fetchItems() {
this.items = [
"A1",
"B2",
"C3"
]
}
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<script src="https://unpkg.com/babel-polyfill/dist/polyfill.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<link href="https://unpkg.com/vuetify#1.0.3/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-layout row wrap>
<v-flex xs6>
<v-subheader>Standard</v-subheader>
</v-flex>
<v-flex xs6>
<v-select :items="items" v-model="selected" label="Select" single-line bottom></v-select>
</v-flex>
<div #click=fetchItems>FetchItems</div>
</v-layout>
</v-container>
</v-app>
</div>
You can modify the items later to update the value.
You can test with clicking the FetchItem to see the effect.
Watchers will do your work, add a watcher to your model and whenever it changes add value to your items.
You can refer this pen, it is using v-combobox which is having power of autocomplete.
https://codepen.io/saurabhtalreja/pen/yLMyJmE
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
select: ['Vuetify'],
items: [
'Programming',
'Design',
'Vue',
'Vuetify',
],
}
},
watch:{
select(val,prev){
console.log('New Input Value is' ,val);
console.log('Previous Value is' ,prev);
console.log('Model is = New Input ie. ', this.select);
console.log('Items Array is', this.items)
this.items.push(val);
console.log('New Items are', this.items)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.4.11/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-row>
<v-col cols="12">
<v-combobox
v-model="select"
:items="items"
label="Combobox"
outlined
dense
></v-combobox>
</v-col>
</v-row>
</v-container>
</v-app>
</div>
If you have data in an array variable named values, then just assign it to IntValues.
ie,
this.IntValues = values;
If you have a single object say value, then just push it to the IntValues. ie, this.IntValues.push(value).

Why can't this component access its props data?

When the following component was inline, it was able to access rowData but as a referenced template it can't:
HTML:
<div id="app">
<div>
<row-component v-for="(row, index) in rows" :row-data="row" v-on:delete-row="deleteThisRow(index)"></row-component>
</div>
</div>
<script id="theRow" type="x-template">
row component: {{rowData}}
<button #click="$emit('delete-row')">Delete3</button>
</script>
JavaScript:
Vue.component('row-component', {
props: ["rowData"],
template: '#theRow'
})
new Vue({
el: '#app',
data: {
rows: ["line1", "line2", "line3", "line4", "line5"]
},
methods: {
deleteThisRow: function(index) {
this.rows.splice(index, 1);
}
}
})
How can I get the component to recognize rowData?
https://jsfiddle.net/edwardtanguay/k1tx3z1n/
Components should have only one root element
<div id="app">
<div>
<row-component v-for="(row, index) in rows" :row-data="row" v-on:delete-row="deleteThisRow(index)"></row-component>
</div>
</div>
<script id="theRow" type="x-template">
<div>
row component: {{rowData}}
<button #click="$emit('delete-row')">Delete3</button>
</div>
</script>
See the working fiddle