Trying to validate a group of inputs, where a couple of them are a custom input that can be repeated. Basically it's a page where you can "Add a person" and enter his details such as Name, address, mobile, email, etc. A person will one have one name, but he can have multiple mobile numbers or emails, so I created a custom input that can get repeated. The AddPerson.vue page looks something like this:
<div class="form-group">
<input v-model="person.name" v-validate="'required'" data-vv-delay="500" name="name" type="text" placeholder="Name">
<span v-show="errors.has('name')" class="input__error">{{ errors.first('name') }}</span>
</div>
<repeatable-inputs v-model="person.mobiles" v-validate="'required'" data-vv-value-path="input" data-vv-name="mobile" type="tel" name="mobile" placeholder="Mobile" :inputmode="numeric" link-name="mobile number"></repeatable-inputs>
All these inputs write the date to a person object sitting on the same page, like so:
export default {
data: function() {
return {
person: {
name: '',
mobiles: [''],
emails: [''],
address: '',
}
The repeatable input currently looks like this:
<template>
<div>
<div class="form-group" v-for="(input, index) in inputs">
<input
:type="type"
:name="name+index"
:placeholder="placeholder"
:inputmode="inputmode"
:value="input"
@input="update(index, $event)"
v-focus></input>
<span v-show="errors.has('name+index')" class="input__error">{{ errors.first('name+index') }}</span>
</div>
<a href="#" v-on:click.prevent="add">Add another {{linkName}}</a>
</div>
</template>
<script>
export default {
props: {
value: {
type: Array,
default: ['']
},
type: {
type: String,
default: "text"
},
name: String,
placeholder: String,
inputmode: String,
linkName: {
type: String,
default: "input"
}
},
data: function () {
return {
inputs: this.value,
}
},
methods: {
add: function () {
this.inputs.push('');
},
update: function(index, e) {
this.inputs[index] = e.target.value;
}
}
</script>
My problem is probably something to do with data-vv-value-path, as I can't seem to point it to the right place
Described above
I'm curious, why do you define inputs instead of just using the value prop directly.
The reasoning was that I needed to store those values inside the component, while also being able to read from the parent. Apparently I can use v-for=(input, index) in value and it still works. The thing is that vue is still somewhat new to me, so it's safe to assume I made a few mistakes along the way.
I created a fiddle with some modifications so it achieves what you were trying to do:
https://jsfiddle.net/logaretm/h61nmtkb/
First lets talk about the approach, with custom components there are two ways to validate them, either they validate themselves, or they get validated by their parent.
In your code, you were validating them by their parent, since you were adding vee-validate in the parent template. But you want to display errors inside the component itself which is a contradiction in my opinion. So lets pick an approach and stick with it. Since it is usually easier to create components that validate themselves you need to do few things:
validate directive to be inside the component's template, so it validates the field directly.rules property to pass down the rules we wish to validate our component with.On a side note you had issues with v-show="errors.has('name+index')" which you can fix by removing the quotations, because with them you are actually passing name+index literally which does not exist.
If you are however interested in making the component act as an input, its a different story:
https://jsfiddle.net/logaretm/u3evvzpL/2/
As you can see the value-path is inputs which is the value that represents the current component's value, in your case It is an array of values, which is perfectly fine.
@logaretm I am following this issue with interest too. For your second fiddle, the error messages for the mobile field are not being rendered.
@pymarco sorry about that:
https://jsfiddle.net/logaretm/u3evvzpL/3/
The issue was that when passing an array as a value, you should clone it first, and pass the clone since vue treats them as the same value (reference equality), and that is why the validator does not detect them unless the component UI state changes.
Also a sidenote, since you are using v-model on your component, you don't have to use data-vv-value-path as the model will be watched for changes instead of the component internal state.
@logaretm Thanks for the fiddles, they helped clear some questions I had about this. I had thought previously about validating the custom input itself, instead of having the parent validate it from outside, but I was missing the injection of the $validator . For my use case I think it's best to use your first example instead of the second, just because I prefer to have each input being validated on it's own, instead of as a group.
@ricardo-afonso Thanks, I realize that the documentation does not cover such advanced topics. We are currently designing a new docs with better structure.
Most helpful comment
I created a fiddle with some modifications so it achieves what you were trying to do:
https://jsfiddle.net/logaretm/h61nmtkb/
First lets talk about the approach, with custom components there are two ways to validate them, either they validate themselves, or they get validated by their parent.
In your code, you were validating them by their parent, since you were adding
vee-validatein the parent template. But you want to display errors inside the component itself which is a contradiction in my opinion. So lets pick an approach and stick with it. Since it is usually easier to create components that validate themselves you need to do few things:validatedirective to be inside the component's template, so it validates the field directly.rulesproperty to pass down the rules we wish to validate our component with.On a side note you had issues with
v-show="errors.has('name+index')"which you can fix by removing the quotations, because with them you are actually passingname+indexliterally which does not exist.If you are however interested in making the component act as an input, its a different story:
https://jsfiddle.net/logaretm/u3evvzpL/2/
As you can see the
value-pathisinputswhich is the value that represents the current component's value, in your case It is an array of values, which is perfectly fine.