Creating components with attribute inheritance
Since Vue 2, it has been possible to use attribute inheritance on components, but in Vue 3, attribute inheritance was made better and with a more reliable API to use in the components.
Attribute inheritance in components is a pattern that provides faster development of custom components based on HTML elements (such as custom inputs, buttons, text wrappers, or links).
In this recipe, we will create a custom input component with attribute inheritance applied directly to the input HTML element.
How to do it...
Here, we will create a component that will have a full attribute inheritance on a selected element on the DOM tree:
- Using the base example from the Creating the base file section, create a new file named component.html and open it.
- In the empty <script> HTML element, create the constants of the functions that will be used using the object destructuring method, calling the defineComponent and createApp methods from the Vue global constant:
const {
defineComponent,
createApp,
} = Vue;
- Create a constant named nameInput, defined as the defineComponent method, passing a JavaScript object as an argument with four properties: name, props, template, and inheritAttrs. Then, we define the value of inheritAttrs as false:
const nameInput = defineComponent({
name: 'NameInput',
props: {},
inheritAttrs: false,
template: ``
});
- In the props property, add a property called modelValue and define it as String:
props: {
modelValue: String,
},
- In the template property, within the template string, we need to do the following:
- Create a label HTML element and add an input HTML element as a child.
- In the input HTML element, define the v-bind directive as a JavaScript object with the destructed value of this.$attrs.
- Define the variable attribute value as the received prop's modelValue.
- Set the input attribute type as "text".
- To the change event listener, add an anonymous function, which receives an event as the argument, and then emit an event called "update:modeValue" with the payload event.target.value:
template: `
<label>
<input
v-bind="{
...$attrs,
}"
:value="modelValue"
type="text"
@change="(event) => $emit('update:modelValue',
event.target.value)"
/>
</label>`
- Create a constant named appComponent, defined as the defineComponent method, passing a JavaScript object as an argument with two properties, data and template:
const component = defineComponent({
data: () => ({}),
template: ``,
});
- In the data property, define it as a singleton function, returning a JavaScript object with a property named name, with the default value as '':
data: () => ({
name: ''
}),
- In the template property, within the template string, we need to do the following:
- Create a NameInput component with a v-model directive bounded to the name data property.
- Create a style attribute with the value "border:0; border-bottom: 2px solid red;".
- Create a data-test attribute with the value "name-input":
template: `
<name-input
v-model="name"
style="border:0; border-bottom: 2px solid red;"
data-test="name-input"
/>`
- Create a constant named app, and define it as the createApp function, passing the component constant as the argument. Then, call the app.component function, passing as the first argument the name of the component you want to register, and as the second argument the component. Finally, call the app.mount function, passing "#app" as the argument:
const app = createApp(component);
app.component('NameInput', nameInput);
app.mount('#app');
How it works...
In Vue 3, in order to create a component, we need to execute the defineComponent function, passing a JavaScript object as an argument. This object maintains almost the same component declaration structure as Vue 2. In the examples, we used the same properties, data, methods, props, and template, all present in the V2.
We used the inheritAttrs property to block the auto application of the attributes to all elements on the components, applying them just to the element with the v-bind directive and with the this.$attrs object deconstructed.
To register the component in the Vue application, we first created the application with the createApp API and then executed the app.component function to register the component globally on the application, prior to rendering our application.