Custom Directives
The Basics
Vue.js allows you to register custom directives, essentially enabling you to teach Vue new tricks on how to map data changes to DOM behavior. You can register a global custom directive with the Vue.directive(id, definition)
method, passing in a directive id followed by a definition object. A definition object can provide several hook functions (all optional):
- bind: called only once, when the directive is first bound to the element.
- update: called for the first time immediately after
bind
with the initial value, then again whenever the binding value changes. The new value and the previous value are provided as the argument. - unbind: called only once, when the directive is unbound from the element.
Example
1 | Vue.directive('my-directive', { |
Once registered, you can use it in Vue.js templates like this (you need to add the Vue.js prefix to it):
1 | <div v-my-directive="someValue"></div> |
When you only need the update
function, you can pass in a single function instead of the definition object:
1 | Vue.directive('my-directive', function (value) { |
All the hook functions will be copied into the actual directive object, which you can access inside these functions as their this
context. The directive object exposes some useful properties:
- el: the element the directive is bound to.
- vm: the context ViewModel that owns this directive.
- expression: the expression of the binding, excluding arguments and filters.
- arg: the argument, if present.
- raw: the raw, unparsed expression.
- name: the name of the directive, without the prefix.
You should treat all these properties as read-only and refrain from changing them. You can attach custom properties to the directive object too, but be careful not to accidentally overwrite existing internal ones.
An example of a custom directive using some of these properties:
1 | <div id="demo" v-demo="LightSlateGray : msg"></div> |
1 | Vue.directive('demo', { |
Result
Multiple Clauses
Comma separated arguments are bound as multiple directive instances. In the following example, directive methods are called twice:
1 | <div v-demo="color: 'white', text: 'hello!'"></div> |
You can achieve single binding with all arguments by closing value with object literal:
1 | <div v-demo="{color: 'white', text: 'hello!'}"></div> |
1 | Vue.directive('demo', function (value) { |
Literal Directives
If you pass in isLiteral: true
when creating a custom directive, the attribute value will be taken as a literal string and assigned as that directive’s expression
. The directive will not attempt to setup data observation.
Example:
1 | <div v-literal-dir="foo"></div> |
1 | Vue.directive('literal-dir', { |
Dynamic Literal
However, in the case that the literal directive contains mustache tags, the behavior is as follows:
The directive instance will have a flag
this._isDynamicLiteral
set totrue
;If no
update
function is provided, the mustache expression will be evaluated only once and assigned tothis.expression
. No data observation happens.If an
update
function is provided, the directive will setup data observation for that expression and callupdate
when the evaluated result changes.
Two-way Directives
If your directive expects to write data back to the Vue instance, you need to pass in twoWay: true
. This option allows the use of this.set(value)
inside the directive:
1 | Vue.directive('example', { |
Inline Statements
Passing in acceptStatement:true
enables your custom directive to accept inline statements like v-on
does:
1 | <div v-my-directive="a++"></div> |
1 | Vue.directive('my-directive', { |
Use this wisely though, because in general you want to avoid side-effects in your templates.
Deep Observation
If your custom directive is expected to be used on an Object, and it needs to trigger update
when a nested property inside the object changes, you need to pass in deep: true
in your directive definition.
1 | <div v-my-directive="obj"></div> |
1 | Vue.directive('my-directive', { |
Directive Priority
You can optionally provide a priority number for your directive (defaults to 0). A directive with a higher priority will be processed earlier than other directives on the same element. Directives with the same priority will be processed in the order they appear in the element’s attribute list, although that order is not guaranteed to be consistent in different browsers.
You can checkout the priorities for some built-in directives in the API reference. Additionally, logic control directives v-if
and v-repeat
are considered “terminal” and they always have the highest priority in the compilation process.
Element Directives
In some cases, we may want our directive to be used in the form of a custom element rather than as an attribute. This is very similar to Angular’s notion of “E” mode directives. Element directives provide a lighter-weight alternative to full-blown components (which are explained later in the guide). You can register a custom element directive like so:
1 | Vue.elementDirective('my-directive', { |
Then, instead of:
1 | <div v-my-directive></div> |
We can write:
1 | <my-directive></my-directive> |
Element directives cannot accept arguments or expressions, but it can read the element’s attributes to determine its behavior.
A big difference from normal directives is that element directives are terminal, which means once Vue encounters an element directive, it will leave that element and all its children alone - only the element directive itself will be able to manipulate that element and its children.
Next, we’ll see how to write a custom filter.