Drie Basis Tools

In Vue's template (HTML) heb je drie tools nodig om data en gedrag aan je HTML te koppelen:

  • Mustache {{ }} — toon data als tekst
  • v-bind (afkorting :) — koppel data aan een HTML attribuut
  • v-on (afkorting @) — luister naar events

Onthoud deze drie:

{{ value }}        <!-- tekst in HTML -->
:attr="value"      <!-- attribuut binden -->
@event="handler"   <!-- event afhandelen -->

Mustache {{ }} — Tekst tonen

De dubbele accolades (mustache) gebruik je om de waarde van een variabele als tekst in je template te zetten.

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

const name = ref('Jason')
const age = ref(25)
</script>

<template>
  <h1>Hallo {{ name }}!</h1>
  <p>Je bent {{ age }} jaar oud.</p>
</template>

Resultaat:

<h1>Hallo Jason!</h1>
<p>Je bent 25 jaar oud.</p>

Belangrijk: Mustache werkt alleen in de inhoud van een tag, niet binnen attributen. Voor attributen gebruik je v-bind.

Werkt niet:

<img src="{{ imageUrl }}" />  <!-- ❌ Verkeerd -->

Wel goed (zie v-bind):

<img :src="imageUrl" />       <!-- ✅ -->

v-bind (:) — Attributen binden

Met v-bind koppel je een variabele aan een HTML attribuut. In de praktijk gebruik je bijna altijd de afkorting :.

Volledige vs Korte syntax

<!-- Volledig -->
<img v-bind:src="imageUrl" />

<!-- Kort (zo gebruik je het) -->
<img :src="imageUrl" />

Praktisch voorbeeld

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

const imageUrl = ref('/photo.jpg')
const altText = ref('Een mooie foto')
const isDisabled = ref(true)
const linkUrl = ref('https://vuejs.org')
</script>

<template>
  <img :src="imageUrl" :alt="altText" />
  <button :disabled="isDisabled">Klik</button>
  <a :href="linkUrl">Vue website</a>
</template>

Regel: Zonder : is het een gewone HTML string. Met : is het JavaScript.

<img src="imageUrl" />    <!-- letterlijk de tekst "imageUrl" -->
<img :src="imageUrl" />   <!-- de waarde van de variabele -->

v-on (@) — Events afhandelen

Met v-on reageer je op gebeurtenissen zoals clicks, inputs en keyboard. De afkorting @ is de standaard.

Volledige vs Korte syntax

<!-- Volledig -->
<button v-on:click="handleClick">Klik</button>

<!-- Kort (zo gebruik je het) -->
<button @click="handleClick">Klik</button>

Praktisch voorbeeld

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

const count = ref(0)

const increment = () => {
  count.value++
}
</script>

<template>
  <p>Je hebt {{ count }} keer geklikt</p>

  <!-- 1. Functie referentie -->
  <button @click="increment">+1</button>

  <!-- 2. Inline expressie -->
  <button @click="count++">+1 (inline)</button>

  <!-- 3. Functie met argument -->
  <button @click="count += 5">+5</button>
</template>

Andere events

<input @input="handleInput" />
<form @submit="handleSubmit">...</form>
<div @mouseover="handleHover">Hover me</div>
<input @keyup.enter="handleEnter" />  <!-- met modifier -->

Modifiers zoals .enter, .stop, .prevent zijn handige korte schrijfwijzen. Bekijk de Events pagina voor meer details.

JavaScript Expressies binnen {{ }}

Tussen de mustache mag je elke JavaScript expressie gebruiken — niet alleen variabelen.

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

const firstName = ref('Anna')
const lastName = ref('Jansen')
const items = ref(['appel', 'banaan', 'kers'])
</script>

<template>
  <!-- String concatenatie -->
  <p>Volledige naam: {{ firstName + ' ' + lastName }}</p>

  <!-- Template literals -->
  <p>{{ `Hallo, ${firstName}!` }}</p>

  <!-- Methode aanroepen -->
  <p>In hoofdletters: {{ firstName.toUpperCase() }}</p>

  <!-- Ternary operator -->
  <p>{{ items.length > 0 ? 'Er zijn items' : 'Leeg' }}</p>

  <!-- Array methodes -->
  <p>Aantal: {{ items.length }}</p>
  <p>Eerste: {{ items[0] }}</p>
</template>

Wat mag NIET tussen {{ }}:

  • Statements: {{ if (x) { ... } }}
  • Variabelen declareren: {{ const x = 5 }}

Alleen expressies die een waarde teruggeven.

Class & Style Binding (handige v-bind trucjes)

v-bind heeft slimme features voor class en style. Hier komt Vue's reactivity echt tot zijn recht.

Class binding met object

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

const isActive = ref(true)
const hasError = ref(false)
</script>

<template>
  <div :class="{ active: isActive, error: hasError }">
    Hallo
  </div>
</template>

Resultaat (omdat isActive true is, hasError false):

<div class="active">Hallo</div>

Class binding met array

<script setup>
const baseClass = 'card'
const variantClass = 'card--large'
</script>

<template>
  <div :class="[baseClass, variantClass]">
    Inhoud
  </div>
</template>

Inline style binding

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

const color = ref('red')
const fontSize = ref(20)
</script>

<template>
  <p :style="{ color: color, fontSize: fontSize + 'px' }">
    Gekleurde tekst
  </p>
</template>

Tip: Class binding is een van de elegantste features van Vue. Je hoeft geen className ? "active" : "" ternary te schrijven zoals in React — gewoon een object met booleans.

Vue vs React — Side-by-side

Voor wie React al kent: dit is hoe template syntax vertaalt.

Tekst tonen

<!-- React (JSX) -->
<h1>Hallo {name}!</h1>

<!-- Vue -->
<h1>Hallo {{ name }}!</h1>

Attribuut binden

<!-- React (JSX) -->
<img src={imageUrl} alt={altText} />

<!-- Vue -->
<img :src="imageUrl" :alt="altText" />

Event afhandelen

<!-- React (JSX) -->
<button onClick={handleClick}>Klik</button>

<!-- Vue -->
<button @click="handleClick">Klik</button>

Conditioneel class

<!-- React (JSX) -->
<div className={isActive ? "card active" : "card"}>...</div>

<!-- Vue -->
<div :class="['card', { active: isActive }]">...</div>

Het grote verschil: In React gebruik je overal JavaScript-expressies tussen { }. In Vue is er onderscheid: {{ }} voor tekst, : voor attributen, @ voor events. Eenmaal door, is het heel snel te lezen.