As you know, v-model is shorthand for v-on:input and v-bind:value="value" on a given element. It allows us to two-way bind a particular element's value, and the events that it emits to one of our internal state properties.
When talking about component composition, however, we need to take extra things into consideration.
In order for a custom component to be able to implement the v-model contract, we have to make sure that two things happen. That's right! We need to ensure that the component has a value property and that it $emits an input event.
There is a way to change this default behavior by using the model property, but it is out of the scope of this book. If you want to tell your component to use a different property, or a different event for v-model, take a look at https://vuejs.org/v2/api/#model.
Let's put this theory into practice. We're going to modify our BaseInput component, in order to be able to use a v-model binding. First, let's add a value property, and hook it to <input>:
props: {
label: {
type: String,
required: true
},
type: {
type: String,
default: 'text',
validator(value) {
return ['text', 'email', 'password'].includes(value);
}
},
// Add this new prop
value: {
type: String,
required: true
}
}
Now that we have our new value prop, we need to bind it to the value of <input>. Be sure to remove the old v-model from it, though! Have a look at the following example:
<input :value="value" type="text" class="form-control">
Almost there; now we need to make sure that <input> dispatches input events whenever it updates. So, we need to add an event handler that $emits this information.
The Vue documentation makes it super clear, so I'm going to quote it:
"v-model internally uses different properties and emits different events for different input elements:
- text and textarea elements use value property and input event;
- checkboxes and radiobuttons use checked property and change event;
- select fields use value as a prop and change as an event."
Now that we have got that theory out of the way, let's actually listen to our event:
<input
:value="value"
:type="type"
class="form-control"
@input="$emit('input', $event.target.value)"
>
Congratulations! Our BaseInput component is ready to be used.
Now that we have a clear understanding of v-model and custom components, we're going to get to use our component inside our form. It will make it far more readable, dynamic, and easy to maintain.