Vue Directives

By

Vue templates are HTML plus directives. Learn v-bind, v-on, v-if, v-for, v-model, and how to register custom directives on a Vue 3 app.

~~~

We saw in Vue templates and interpolations how you embed data in templates.

This article covers the other tool Vue gives you in templates: directives.

Directives look like HTML attributes. They start with v-.

v-text

Instead of interpolation, you can use v-text:

<span v-text="name"></span>

v-once

You know how {{ name }} binds to the name property of the component state. Any time name changes, Vue updates the value in the browser.

Unless you use v-once, which renders the element once and skips later updates:

<span v-once>{{ name }}</span>

v-html

Interpolation escapes HTML. That helps against XSS.

There are cases however where you want to output HTML and make the browser interpret it. You can use the v-html directive, but only with content you trust:

<span v-html="someHtml"></span>

v-bind

Interpolation works in tag content, not in attributes.

Attributes use v-bind:

<a v-bind:href="url">{{ linkText }}</a>

Shorthand:

<a :href="url">{{ linkText }}</a>

Two-way binding using v-model

v-model binds a form control to state. User input updates the data:

<input v-model="message" placeholder="Enter a message">
<p>Message is: {{ message }}</p>
<select v-model="selected">
  <option disabled value="">Choose a fruit</option>
  <option>Apple</option>
  <option>Banana</option>
  <option>Strawberry</option>
</select>
<span>Fruit chosen: {{ selected }}</span>

Using expressions

You can use any JavaScript expression inside a directive:

<span v-text="'Hi, ' + name + '!'"></span>
<a v-bind:href="'https://' + domain + path">{{ linkText }}</a>

Variables in directives refer to component state (or bindings from <script setup>).

Conditionals

Ternaries work inside directives:

<span v-text="name === 'Flavio' ? 'Hi Flavio!' : 'Hi ' + name + '!'"></span>

For bigger branches use v-if, v-else-if, and v-else:

<p v-if="shouldShowThis">Hey!</p>

shouldShowThis is a boolean in your state. v-if removes the element from the DOM when false. Prefer v-show if you only need to toggle CSS visibility often.

Loops

v-for allows you to render a list of items. Give each item a stable :key, so Vue can tell the elements apart when the list changes:

<template>
  <ul>
    <li v-for="item in items" :key="item">{{ item }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: ['car', 'bike', 'dog']
    }
  }
}
</script>

Or an array of objects:

<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      todos: [
        { id: 1, title: 'Do something' },
        { id: 2, title: 'Do something else' }
      ]
    }
  }
}
</script>

You can also get the index:

<li v-for="(todo, index) in todos" :key="todo.id"></li>

Avoid putting v-if and v-for on the same element. In Vue 3 v-if runs first, so it cannot see the loop variable (in Vue 2 it was the other way around). Filter the list in a computed property instead, or move the v-for to a wrapping <template> tag.

Events

v-on listens to DOM events:

<template>
  <a v-on:click="handleClick">Click me!</a>
</template>

<script>
export default {
  methods: {
    handleClick() {
      alert('test')
    }
  }
}
</script>

Pass arguments:

<a v-on:click="handleClick('test')">Click me!</a>

Or a single expression inline:

<a v-on:click="counter = counter + 1">{{ counter }}</a>

Shorthand is @:

<a @click="handleClick">Click me!</a>

More details in Vue Events

Show or hide

v-show toggles display: none. The element stays in the DOM:

<p v-show="isTrue">Something</p>

Event directive modifiers

Vue offers some optional event modifiers you can append to v-on / @. They make the event do something without you coding it in the handler.

One good example is .prevent, which calls preventDefault() on the event, so this form does not reload the page:

<form v-on:submit.prevent="formSubmitted"></form>

Others include .stop, .capture, .self, .once, .passive. See the Vue 3 event modifier docs.

Custom directives

Built-in directives cover most cases. For something reusable at the DOM level, register a custom directive on the app:

import { createApp } from 'vue'

const app = createApp({})

app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

app.mount('#app')
<input v-focus />

In Vue 3 the hook names match the component lifecycle (mounted, updated, unmounted, …), not the old Vue 2 bind / inserted names.

More detail: Custom Directives.

Tagged: Vue.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about vue: