Using Reactive Data in Templates

 

All of the following Vue template syntax is valid HTML that can be parsed by compliant browsers.   Vue provides text interpolation, built-in directives that reactively apply updates to the DOM when the value of the specified expression changes, and modifiers that specify how directives should be bound.

Below I pull examples the Vue Template Syntax tutorial.

Text Interpolation

As we saw in the last tutorial we can bind state properties to DOM elements using mustache syntax.  When the value of the property changes the DOM is automatically updated.

<span>Message: {{ msg }}</span>

Vue supports any valid JavaScript expression that can be evaluated to a value inside all data bindings.

<span>{{ `Message: ${msg}` }}</span>
<span>{{ 'Message: ' + msg }}</span>
<span>Message: {{ msg.toUpperCase() }}</span>
<span>Message: {{ getNextMessage() }}</span>

Template expressions have access to a restricted list of globals including Math and Date. Properties attached to window are not accessible, but you can define globals by adding them to app.config.globalProperties.

Attribute Bindings

Mustache syntax cannot used inside HTML tags, so to bind a state property to an attribute or component prop we use a special directive, the v-bind directive.  v-bind statements, as do other directive statements, have the following form.

directive:argument.modifier="value"

A typical v-bind statements that binds a state property to an element’s attribute or property is shown below.

v-bind:attrOrProp="stateProp"

The v-bind directive is used so frequently, Vue allows a : alone to specify the v-bind directive.

Let’s look at an example. Below we declare a state property named textColor and a function named toggleColor() that toggles the value of textColor between ‘red’ and ‘blue’.  We also define two CSS classes named red and blue.

// in <script>
const textColor = ref("blue")

function toggleColor() {
  textColor.value = (textColor.value == 'blue') ? "red" : "blue"
}

// in <style>
.blue {
  color: blue;
}
.red {
  color: red;
}

Below we add a button and then add a span which binds textColor to the element’s class attribute.  When the value of textColor is ‘red’, the span’s class attribute becomes ‘red’ and when the value of textColor is ‘blue’, the span’s class attribute becomes ‘blue’.

<button @click="toggleColor">Toggle</button>
<span v-bind:class="textColor">First Item</span>

When used to bind the class or style attributes, the value may be an array or object. One use of arrays is to bind multiple state properties to an element. Below we declare two state properties.

const fontColor = reactive({ color: 'blue' })
const fontSize = reactive({ 'font-size': '20px'})

We bind the :style to an array containing the two state properties above.

<h1 :style="[fontColor, fontSize]">Hello</h1>

Please review the v-bind API documentation and the documentation on class and style binding for the various ways to use arrays and objects when declaring class and style attributes.

The class and style binding documentation also explains that component elements can also have class attributes.  If the template of the component has a single root element then the classes given to the component element are added to the classes of the root element.  If the component template has multiple root elements then you need to define which element will receive the class names using the $attrs component property in the template root element’s class attribute.

Registering Event Listeners

We can register an event listener with the v-on directive.  For v-on, the argument that follows the : is the name of the event.

<button v-on:click="toggleColor">Toggle</button>

We can also use the shorthand notation and replace v-on: with @ symbol.

<button @click="toggleColor('red')">Toggle</button>

If the handler string is a JavaScript identifier, as in the first example, then the string is treated as a method handler.  If the handler string is a function call, as in the second example, it is treated as an inline handler.

Method handlers are only passed the native DOM event that triggered the handler, whereas inline handlers can be passed custom arguments as well as the event.  To access the event in inline handlers we use the $event variable as shown below.

<button @click="toggleColor('red', $event)">Toggle</button>

We can also use state properties to define the event to be listed for.  In the example below the event is stored in the state property named currentEvent which is initialized to “click’.   When the function named changeEvent is called, the value in currentEvent is toggled between “click” and “dblclick”.

const currentEvent = ref("click")

function changeEvent() {
  currentEvent.value = (currentEvent.value === "click") ? "dblclick" : "click"
}

In our template we can specify that the event that should be listed for is the value in currentEvent by surrounding the state property in [].

<button @[currentEvent]="changeEvent" class="me-2">{{ currentEvent }}</button>

When v-on is used on normal DOM elements, only native DOM events can be used and if v-on is used on a component element then only custom events (discussed later) can be used.

Vue provide a number of key modifiers that register handlers for specific keyboard key presses.  Please review the v-on API documentation for the various arguments and modifiers that can be used with the v-on directive.

Computed Properties

Putting too much inline JavaScript in templates can make the code hard to maintain and may introduce duplication.

Computed properties are defined in the script tag, take getter functions as arguments and return computed refs.  Similar to normal refs, you can access their value through the value property.  Also like normal refs, Vue tracks the reactive dependencies in computed refs and updates the DOM when their values change and like normal refs, computed refs are auto-unwrapped in templates.

When using computed properties we need to import the computed from vue.

import { computed } from 'vue'

Below is an example showing how computed properties can be used.  In the example we create a reactive array, a function named addItem that adds a random integer to the array, and a computed property named numberOfItems whose value is a string specifying the number of elements in the array.

const items = reactive([3,5,12])

function addItem() {
  items.push(Math.floor(Math.random() * 20))
}

const numberOfItems = computed(() => {
  return `Number of items: ${items.length}`
})
We then use the text interpolation syntax to display the string returned by the getter function passed to computed.  Whenever the button is pressed, the reactive array is modified, triggering Vue to recompute the value of numberOfItems and update the DOM.
<button @click="addItem"class="me-2">Add Item</button>
<span>{{ numberOfItems }}</span>
The difference between computed properties and normal methods are that normal methods are always run whenever a re-render occurs, whereas computed properties are only reevaluated when the reactive dependencies change.

© 2023, Eric.