Vue
07 / 08

Directives & Template Syntax

Vue Directives & Template Syntax

Vue's template syntax and built-in directives provide powerful ways to declaratively bind data to the DOM:

Text Interpolation & v-bind

<template>
  <!-- Text interpolation -->
  <p>{{ message }}</p>
  <p>{{ count * 2 }}</p>
  <p>{{ ok ? 'YES' : 'NO' }}</p>
  
  <!-- v-bind for attributes -->
  <img v-bind:src="imageSrc" v-bind:alt="imageAlt" />
  
  <!-- Shorthand -->
  <img :src="imageSrc" :alt="imageAlt" />
  
  <!-- Dynamic attributes -->
  <button :[attributeName]="value">Button</button>
  
  <!-- Binding multiple attributes -->
  <div v-bind="objectOfAttrs"></div>
  
  <!-- Class binding -->
  <div :class="{ active: isActive, 'text-danger': hasError }"></div>
  <div :class="[activeClass, errorClass]"></div>
  
  <!-- Style binding -->
  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  <div :style="[baseStyles, overridingStyles]"></div>
</template>

Conditional Rendering

<template>
  <!-- v-if: Conditional rendering (removes from DOM) -->
  <div v-if="type === 'A'">
    Type A
  </div>
  <div v-else-if="type === 'B'">
    Type B
  </div>
  <div v-else>
    Type C
  </div>
  
  <!-- v-show: Toggle visibility (CSS display) -->
  <div v-show="isVisible">
    Toggleable content
  </div>
  
  <!-- Template for grouping without extra elements -->
  <template v-if="showSection">
    <h1>Title</h1>
    <p>Content</p>
  </template>
  
  <!-- Use v-if for infrequent toggles, v-show for frequent -->
  <Modal v-if="showModal" /> <!-- Heavy component, unmount when hidden -->
  <Tooltip v-show="showTooltip" /> <!-- Light component, keep in DOM -->
</template>

List Rendering

<template>
  <!-- Basic v-for -->
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
  
  <!-- v-for with index -->
  <li v-for="(item, index) in items" :key="item.id">
    {{ index }}. {{ item.text }}
  </li>
  
  <!-- v-for with object -->
  <div v-for="(value, key, index) in user" :key="key">
    {{ index }}. {{ key }}: {{ value }}
  </div>
  
  <!-- v-for with range -->
  <span v-for="n in 10" :key="n">{{ n }}</span>
  
  <!-- v-for with v-if (avoid this pattern) -->
  <!-- ❌ Bad: v-if with v-for on same element -->
  <li v-for="todo in todos" v-if="!todo.done" :key="todo.id">
    {{ todo.text }}
  </li>
  
  <!-- ✅ Good: Use computed to filter first -->
  <li v-for="todo in activeTodos" :key="todo.id">
    {{ todo.text }}
  </li>
  
  <!-- ✅ Alternative: Use template -->
  <template v-for="todo in todos" :key="todo.id">
    <li v-if="!todo.done">
      {{ todo.text }}
    </li>
  </template>
</template>

Event Handling

<template>
  <!-- Basic event handler -->
  <button @click="handleClick">Click Me</button>
  
  <!-- Inline handler -->
  <button @click="count++">{{ count }}</button>
  
  <!-- Method with parameters -->
  <button @click="greet('Hello')">Greet</button>
  
  <!-- Access event object -->
  <button @click="handleClick($event)">Click</button>
  
  <!-- Event modifiers -->
  <button @click.stop="doThis">Stop Propagation</button>
  <button @click.prevent="doThis">Prevent Default</button>
  <form @submit.prevent="onSubmit">Submit</form>
  <button @click.once="doThis">Only Once</button>
  <div @click.self="doThis">Only if target is self</div>
  
  <!-- Key modifiers -->
  <input @keyup.enter="submit" />
  <input @keyup.tab="handleTab" />
  <input @keyup.delete="handleDelete" />
  <input @keyup.esc="handleEscape" />
  <input @keyup.ctrl.enter="ctrlEnterHandler" />
  
  <!-- Mouse modifiers -->
  <button @click.left="handleLeftClick">Left Click</button>
  <button @click.right="handleRightClick">Right Click</button>
  <button @click.middle="handleMiddleClick">Middle Click</button>
  
  <!-- Modifier chaining -->
  <button @click.stop.prevent="doThis">Chained Modifiers</button>
</template>

Form Input Bindings

<script setup>
import { ref } from 'vue'

const text = ref('')
const checked = ref(false)
const checkedNames = ref([])
const picked = ref('')
const selected = ref('')
const multiSelect = ref([])
</script>

<template>
  <!-- Text input -->
  <input v-model="text" placeholder="Enter text" />
  <p>Text: {{ text }}</p>
  
  <!-- Checkbox -->
  <input type="checkbox" v-model="checked" />
  <p>Checked: {{ checked }}</p>
  
  <!-- Multiple checkboxes -->
  <input type="checkbox" value="Vue" v-model="checkedNames" />
  <input type="checkbox" value="React" v-model="checkedNames" />
  <input type="checkbox" value="Angular" v-model="checkedNames" />
  <p>Checked: {{ checkedNames }}</p>
  
  <!-- Radio -->
  <input type="radio" value="One" v-model="picked" />
  <input type="radio" value="Two" v-model="picked" />
  <p>Picked: {{ picked }}</p>
  
  <!-- Select -->
  <select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  
  <!-- Modifiers -->
  <input v-model.lazy="text" /> <!-- Update on change, not input -->
  <input v-model.number="age" type="number" /> <!-- Convert to number -->
  <input v-model.trim="message" /> <!-- Trim whitespace -->
</template>

Custom Directives

Custom directives allow you to directly manipulate the DOM when needed.

// directives/focus.js
export const vFocus = {
  mounted(el) {
    el.focus()
  },
}

// directives/click-outside.js
export const vClickOutside = {
  mounted(el, binding) {
    el.clickOutsideEvent = (event) => {
      if (!(el === event.target || el.contains(event.target))) {
        binding.value(event)
      }
    }
    document.addEventListener('click', el.clickOutsideEvent)
  },
  unmounted(el) {
    document.removeEventListener('click', el.clickOutsideEvent)
  },
}

// Usage in component
<script setup>
import { vFocus } from './directives/focus'
import { vClickOutside } from './directives/click-outside'

const isOpen = ref(false)

const handleClickOutside = () => {
  isOpen.value = false
}
</script>

<template>
  <input v-focus placeholder="Auto-focused" />
  
  <div v-click-outside="handleClickOutside">
    <button @click="isOpen = true">Open Menu</button>
    <div v-if="isOpen" class="menu">
      Menu content
    </div>
  </div>
</template>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free