Vue / Basic: reactive components and input contracts / Lesson 2
Templates, directives, and safe expressions
Translate state into text, attributes, conditional regions, and events with clear expression boundaries.
On this page
Lesson goals
- Distinguish interpolation, binding, and event handling.
- Choose conditional rendering and stable template structure.
Keep template expressions small and meaningful
Vue templates describe HTML extended with bindings. Text interpolation evaluates an expression and escapes the resulting text rather than treating it as a template or HTML program. Attribute binding uses v-bind or its colon shorthand; event handling uses v-on or the at-sign shorthand. A template expression should be understandable as a value, not a place to hide complex business mutation. Move reusable or expensive derivation into a computed value or function with a clear contract. Templates have a restricted execution context, so do not assume every global JavaScript name is available automatically.
Bind values with the intended type
A literal HTML attribute and a bound expression can have different meaning. A component prop written as a plain string is not the same as a numeric or boolean expression passed through a binding. Native boolean attributes follow presence rules, while Vue bindings coordinate their values with the DOM. The example binds disabled from state and uses a real button for the action. Do not put braces inside an ordinary attribute expecting interpolation to run there. Keep user-facing labels descriptive, and let the browserโs native semantics provide focus and keyboard behavior before adding custom event logic.
<script setup>
import { ref } from "vue"
const saving = ref(false)
const label = ref("Reading notes")
</script>
<template>
<h1>{{ label }}</h1>
<button type="button" :disabled="saving" @click="saving = true">
Save notes
</button>
</template>
In the stated Vue SFC setup, the initial button is enabled. Activating it changes saving to true and disables the button. No persistence is implemented; the example teaches the binding loop rather than claiming a saved record.
Choose creation versus visibility deliberately
v-if creates and destroys its branch as the condition changes, including child component lifecycle. v-show keeps the element rendered and toggles display through style, so it has different initial cost and state lifetime. Neither is a permission boundary: hidden content can still exist in the client and server authorization remains necessary. Choose based on interaction frequency, initialization needs, and component ownership. Adjacent v-else or v-else-if branches must follow the documented structure. A conditional that removes a focused element also needs a meaningful focus destination, especially in forms and dialogs.
Extract a readable display decision
A small pure function can make the state-to-message contract testable without a browser. The example distinguishes loading, failure, empty content, and ready content rather than scattering nested conditions through a template. In a real component, decide whether stale data can remain visible during refresh failure; mutually exclusive labels are only one possible product model. This JavaScript example uses synthetic state and no Vue runtime, making its exact expectations locally verifiable. The template still needs separate verification that it renders the selected message and exposes status changes accessibly.
function displayState({ loading, failed, count }) {
if (loading) return "Loading notes"
if (failed) return "Could not load notes"
return count === 0 ? "No notes yet" : "Notes ready"
}
if (displayState({ loading: false, failed: false, count: 0 }) !== "No notes yet") {
throw new Error("DISPLAY_STATE_MISMATCH")
}
This standalone JavaScript can run under Node without Vue or network access. The assertion passes for the empty state. It does not mount a component or establish screen-reader announcement behavior.
Preserve valid HTML and content boundaries
Vue compiles templates, but browser HTML parsing still matters, especially during server rendering and hydration. Invalid nesting can be repaired by the browser into a structure different from the expected tree. Use semantic elements and avoid mounting on untrusted server-generated templates. v-html deliberately inserts HTML and bypasses ordinary text escaping; use it only for content that has an appropriate trusted or sanitized contract. The value should never become executable Vue template source. Keep security, accessibility, and rendering claims separate and verify each boundary rather than assuming successful compilation proves all three.
Common errors and fixes
Interpolation appears literally in an attribute
- Why it happens
- Text interpolation syntax was used where binding was required.
- How to fix it
- Use the appropriate v-bind expression.
- Prevent it next time
- Distinguish text and attribute syntax during review.
A hidden button is treated as authorization
- Why it happens
- Presentation state is confused with server permission.
- How to fix it
- Enforce the protected action on the server.
- Prevent it next time
- Review client visibility and access control separately.
A conditional loses a draft unexpectedly
- Why it happens
- v-if destroyed the component owning local state.
- How to fix it
- Move ownership or choose visibility semantics intentionally.
- Prevent it next time
- Document state lifetime before selecting the directive.
Check your understanding
1. A frequently toggled help panel contains local input state. Compare v-if and v-show.
Show worked solution
v-if recreates the branch and can reset state owned there; v-show retains the rendered branch while hiding it. Choose based on desired lifetime and cost, and verify focus does not remain in hidden content. Neither choice replaces permission checks.
2. A lesson title contains an ampersand. Should normal interpolation be replaced by v-html?
Show worked solution
No. Ordinary text interpolation is appropriate and escapes text for rendering. Rich HTML is a different requirement with a separate sanitization and trust design; a punctuation character does not justify bypassing the normal text boundary.
Key takeaways
- Template syntax has distinct text, binding, and event forms.
- Conditional rendering changes lifecycle as well as appearance.
- Valid HTML and safe content remain application responsibilities.