Custom Directives in Vue 3
In addition with default directive, we could also create and use custom directive in Vue.js. If you remember, firstly you can compare these directives with HTML Boolean attributes. Something similar to disabled
attribute. The disabled attribute makes HTML element unusable and un-clickable. Ultimately it brings some execution on the elements. Similarly, The custom directive brings some execution on the relevant DOM elements. We can define custom directive as an object containing lifecycle hooks similar to those of a component. Below is the very simple use case,
const focus = {
mounted: (el) => el.focus()
}
export default {
directives: {
// enables v-focus in template
focus
}
}
We can use above directive in the template like below,
<input v-focus />
This is a simple example, we can do lot more with this directives. But, here it will focus the input element assuming you haven’t clicked elsewhere on the page. Let’s take the deep dive into it.
Global Directive Registration
In the above example, we are using local registration. In addition, similar to components, we can globally register custom directives in the application instance. So that we can use the directives through out the application in the templates.
const app = createApp({})
// make v-focus usable in all components
app.directive('focus', {
/* ... */
})
However, we can prefer to use custom directives only when the desired functionality can only be achieved via direct DOM manipulation. Otherwise we could use v-bind because they are more efficient and server-rendering friendly.
Custom Directives in Vue 3 – Hooks
The directive object is providing lot of optional hook functions. For Instance, we would use this hooks to manipulate the DOM, read the old value, component instance and vnode representation as well.
const myDir = {
// called before bound element's attributes
// or event listeners are applied
created(el, binding, vnode, prevVnode) {
// see below for details on arguments
},
// called right before the element is inserted into the DOM.
beforeMount() {},
// called when the bound element's parent component
// and all its children are mounted.
mounted() {},
// called before the parent component is updated
beforeUpdate() {},
// called after the parent component and
// all of its children have updated
updated() {},
// called before the parent component is unmounted
beforeUnmount() {},
// called when the parent component is unmounted
unmounted() {}
}
}
Hook Arguments
- el – We can use el to manipulate the DOM
- binding – it is an object having lot of properties
- value – the value of the directive. Example, v-my-directive=”VALUE”
- oldValue – the old value of the directive. only available in
beforeUpdate
andupdated
- arg – the argument of the directive. Example, v-my-directive:foo
- instance – the instance of the component
- dir – the definition object of directive
- modifiers – the modifier object. Example v-my-directive:foo:bar. Here the modifiers object would be
{ foo: true, bar: true }
- vnode – The V Node representation of the element
- prevNode – The element of previous render. Only available in the
beforeUpdate
andupdated
hooks
Consider the below example,
<div v-example:foo.bar="baz">
// we can represent above directive as below object
{
arg: 'foo',
modifiers: { bar: true },
value: /* value of `baz` */,
oldValue: /* value of `baz` from previous update */
}
// binding value as JavaScript object literal
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
}
Dynamic Argument in Directives
We can also pass dynamic argument to the directives. This is one of the special feature of directive. For example,
<div v-example:[arg]="value"></div>
// Here the directive argument will be reactively updated
based on arg property in our component state.
Most important this here is , other than el, we should not modify other read-only properties. If you want so, you can follow this dataset
Function Shorthand
If we wants only mounted
and updated
behavior of custom directives, then we can use function shorthand there.
<div v-color="color"></div>
app.directive('color', (el, binding) => {
// this will be called for both `mounted` and `updated`
el.style.color = binding.value
})
Custom Directives in Vue 3 – on Component
As similar to DOM elements, we can use custom directives on component as well. Here It will apply to a component root element. Note that, sometimes the component may have more than one root node. If so, it will ignore the directives and a warning will be shown. Above all, it is not preferable to use custom directives on component.
<MyComponent v-demo="test" />
<!-- template of MyComponent -->
<div> <!-- v-demo directive will be applied here -->
<span>My component content</span>
</div>
With Directives
In Addition, we can apply custom directives to v-node using withDirectives
as follows,
import { h, withDirectives } from 'vue'
// a custom directive
const pin = {
mounted() { /* ... */ },
updated() { /* ... */ }
}
// <div v-pin:top.animate="200"></div>
const vnode = withDirectives(h('div'), [
[pin, 200, 'top', { animate: true }]
])
Summary
In Conclusion,
- Firstly, the custom directive brings some execution on the elements. We can define custom directive as an object containing lifecycle hooks similar to those of a component.
- Use custom directives only when the desired functionality can only be achieved via direct DOM manipulation.
- You would pass dynamic argument to the directives. This is one of the special feature of directive.
References
- https://vuejs.org/guide/reusability/custom-directives.html#introduction
- https://vuejs.org/guide/extras/render-function.html#custom-directives