Vue 2 | Changing Displayed Data and Handling Events
This article updates data displayed by Vue.js through events.
- Vue 2.5.4
You Do Not Need to Change the DOM Directly
When data changes, Vue.js handles the work that previously required updating the DOM manually. This is a major feature of data binding libraries such as Vue.js, and it reduces display mistakes and bugs in presentation. In fact, this kind of feature is one of the biggest reasons many developers use JavaScript frameworks.
About the Virtual DOM
Vue.js first breaks down the template used by the application and creates a tree-structured virtual DOM corresponding to the real DOM. Each element in this tree is called a VNode. Even when data changes, Vue does not immediately change the DOM directly. It reflects the synchronization result in the actual DOM asynchronously through the virtual DOM, avoiding unnecessary DOM manipulation and making updates fast.
Because DOM changes are asynchronous, even if you access the DOM synchronously right after changing data, it may still remain in the old state.
Registering Event Handling
To handle events such as “when the mouse is over an element” or “when a button is clicked,” use the v-on directive. It is similar to jQuery’s on.
<button v-on:click="processing or method name">click!</button>
v-on can be abbreviated as @. As with v-bind, it is recommended to write v-on at first and abbreviate it after you become familiar with it.
<button @click="processing or method name">click!</button>
If you can use modifiers, you can also make checks such as prevent or self.
<div @click.self="processing or method name" class="overlay">...</div>
Changing Simple Text
Let us change simple text. In the following example, clicking the button calls the test method defined in methods. The processing increases the count property defined in data.
<div id="app" class="area">
<p>You clicked {{ count }} times.</p>
<button v-on:click="test">click!</button>
</div>
As you can see, there is no code that directly changes the DOM.
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
test: function() {
this.count++
}
}
})
When this.count++ increases the count value, the display is updated together.
Because v-on is also a directive, its value can be an expression. For short code that will not be reused, you can write the expression directly without a method. However, putting code in a template hurts readability, so specifying a method is generally recommended.
<button @click="count++">click!</button>
Using v-model
v-model is a directive that connects form input and data, keeping the connected form input value, selection value, and data synchronized.
<div id="app">
<p>You entered '{{ message }}'.</p>
<p><input type="text" v-model="inputMessage" /><button @click="getMessage">click!</button></p>
</div>
new Vue({
el: '#app',
data: {
message: '',
inputMessage: ''
},
methods: {
getMessage: function() {
this.message = this.inputMessage
}
}
})
Because data is synchronized, if message is connected directly, it changes as soon as text is entered without clicking, unlike the example above.
Using $refs
With $refs, you can refer directly to the DOM. It can also be used with component tags learned later. Be careful: this does not use the virtual DOM, so rendering is not optimized. It accesses and redraws the DOM each time something is manipulated, so it is not suitable for display updates. You will not use it often, but it is useful to remember for cases where you need to reference an element.
If you give a template tag a name with ref, you can refer to it with this.$refs.NAME.
<div id="app">
<p><input type="text" ref="message"> <button @click="getMessage">click!</button></p>
</div>
new Vue({
el: '#app',
methods: {
getMessage: function() {
// Use the input value with ref="message".
alert(this.$refs.message.value)
}
}
})
Use it when you need direct DOM access, such as for an element’s position or height.
Updating List Data
Updating an Array
Try changing the word apple at index 0 in a prepared English word list to uppercase.
<div id="app">
<ul>
<li v-for="val in list">{{ val }}</li>
</ul>
<p><button @click="test">click!</button></p>
</div>
new Vue({
el: '#app4',
data: {
list: ['apple', 'banana', 'strawberry']
},
methods: {
test: function() {
this.list[0] = this.list[0].toUpperCase()
}
}
})
Writing it this way does not seem to work. Because of JavaScript limitations, Vue cannot detect direct array index changes, so use the $set method.
vm.$set(object, key, value)
For this data replacement, write:
this.$set(this.list, 0, this.list[0].toUpperCase())
This time it changes correctly. This behavior is largely because of IE support. In a future version such as Vue.js 3, if IE support is dropped, Vue.set may no longer be needed.
Changing an Object
Objects can be changed without problems.
new Vue({
el: '#app',
data: {
list: {
a: 'apple',
b: 'banana',
c: 'strawberry'
}
},
methods: {
test: function() {
this.list.a = this.list.a.toUpperCase()
}
}
})
If you add a property such as d that did not originally exist, this.$set is also required.
Changing a List of Objects
Now try an array without using $set first.
<div id="app">
<ul>
<li v-for="(val, idx) in list" v-bind:key="val.name">
(No:{{ idx }}) {{ val.name }} {{ val.count }} items
</li>
</ul>
<button v-on:click="test(100, $event)">click!</button>
</div>
v-on handlers can also be run inline with parameters. In this case, $event can pass the original event object.
new Vue({
el: '#app',
data: {
list: [{
name: 'Apple',
count: 0
}, {
name: 'Banana',
count: 0
}, {
name: 'Strawberry',
count: 0
}]
},
methods: {
test: function(count) {
this.list[0].count = count
}
}
})
This works correctly. It seems direct array index replacement is the problem, while monitoring inside objects works.
Changing List Data with v-model
You should not directly specify data obtained with v-for in v-model; in such cases you can use @input instead. However, if the original data is an array, it can be connected.
<div id="app">
<ul>
<li v-for="(val, idx) in list" v-bind:key="idx">
(No:{{ idx }}) {{ val }}
</li>
</ul>
<ul>
<li v-for="(val, idx) in list" v-bind:key="idx">
(No:{{ idx }}) {{ val }} <input size="10" v-model="list[idx]">
</li>
</ul>
</div>
Responding to External Changes Such as jQuery
At first, you may want to use plugins such as jQuery together with Vue. In that case, use JavaScript dispatchEvent.
var eventInput = document.createEvent('UIEvents')
eventInput.initEvent('input', false, false);
$(document).on('click', '[data-update]', function() {
$('#message').val($(this).data('update'))
$('#message')[0].dispatchEvent(eventInput)
});
If it is only a reference, there is no major problem, but operations that rebuild elements, such as table sorting, should be avoided as much as possible. Manipulating the real DOM does not update the data, so it may be corrected by the virtual DOM depending on the situation. In Vue.js, the main point is data, not the DOM. If you want to change list order or filter a list, computed properties introduced in the next chapter make it very easy.
Point to Remember When Updating Data
When adding values one by one to an object, or updating with an array index, it will not react unless you use $set. Unless necessary, it may be easier to update a list of objects at the end.
Conclusion
Linking data and elements is much easier than writing it in jQuery, but as code grows, maintaining data integrity can become difficult. This is probably why Vuex appeared. There is always more to study.
This time we learned how to register events with v-on and the basics of updating data. Next, we will manipulate data through conditional branching.