When developing any application with Vue.js, you probably need to create some kind of input field component for your forms. Moreover, the fields can vary from each other with many details like labels, hints, prefixes, suffixes, icons, validation messages, etc.
From the HTML point of view, the input field itself is not enough to achieve this. Obviously, there is more markup needed. Consider the following code as examples:
Default input:
<input type="text" placeholder="Default input" name="example" />
Code language: HTML, XML (xml)
Input with hint:
<input type="text" placeholder="Default input" name="example" />
<div>Nam pretium mi tempor eros interdum.</div>
Code language: HTML, XML (xml)
Input with label:
<label>
<div>Name</div>
<input type="text" placeholder="Default input" name="example" />
</label>
Code language: HTML, XML (xml)
Input with prefix:
<div>
<div>Prefix</div>
<input type="text" placeholder="Default input" name="example" />
</div>
Code language: HTML, XML (xml)
Input with suffix:
<div>
<input type="text" placeholder="Default input" name="example" />
<div>Suffix</div>
</div>
Code language: HTML, XML (xml)
All in one:
<label>
<div>Name</div>
<div>
<div>Prefix</div>
<input type="text" placeholder="Default input" name="example" />
<div>Suffix</div>
</div>
<div>Nam pretium mi tempor eros interdum.</div>
</label>
Code language: HTML, XML (xml)
Applying v-model directive and listeners to created component
When you apply the v-model directive to the component like this:
<InputField v-model="inputValue"/>
Code language: HTML, XML (xml)
it would not work to update the input value and listen to any changes if the component markup is wrapped with some additional elements (like in the example). This happens because of the default attributes and listeners inheritance behavior. Vue would bind those to the component wrapping element out of the box. This is a problem when we want to update the form state by listening to the input event or pass the value by the v-model. Fortunately, the behavior can be adjusted with a component property called inheritAttrs. You can use it in the component script alongside data, methods, watchers, lifehooks, etc.:
<script>
export default {
name: 'input-field',
data: {
inputValue: ''
},
inheritAttrs: false
// [...]
}
</script>
Code language: JavaScript (javascript)
With inheritAttrs attribute set to false, Vue would not bind the attributes and listeners to the wrapping element. Instead, a programmer can decide where those should be bound. To do this, bind global variables $attrs and $listeners to the input element:
<label>
<div>Name</div>
<div>
<div>Prefix</div>
<input type="text" placeholder="Default input" name="example" v-bind="$attrs" v-on="$listeners" />
<div>Suffix</div>
</div>
<div>Nam pretium mi tempor eros interdum.</div>
</label>
Code language: HTML, XML (xml)
With this, all events would be emitted to the parent component, and props would be passed down to the element.
By default, parent scope attribute bindings that are not recognized as props will “fallthrough” and be applied to the root element of the child component as normal HTML attributes. When authoring a component that wraps a target element or another component, this may not always be the desired behavior. By setting inheritAttrs to false, this default behavior can be disabled. The attributes are available via the $attrs instance property (also new in 2.4) and can be explicitly bound to a non-root element using v-bind. Note: this option does not affect class and style bindings.
https://vuejs.org/v2/api/#inheritAttrs
Leave a Reply