Implementing Two-Way Computed Properties In Vue.js
A guide on how to use model directives with two-way computed properties in Vue.js
Introduction
If you are a Vue.js developer like me, you must have heard about the very interesting v-model directive which is gaining popularity in the Vue.js world.
The v-model directive is a very useful and clean approach for binding any changes to a value as well as listening to the changes that may affect that value. In technical terms, you can say that the v-model is a powerful directive provided by Vue.js to add two-way data binding in vanilla Vue.js.
Implementing the v-model directive with a computed property
Before starting with the implementation of the V-model, we have to understand the under-hood working of the V-model directive.
- While
v-model=”dataProperty”
seems like magic at first glance, it’s actually a shorthand for the<input :value="dataProperty" @input=dataProperty = $event.target.value" />
- By default, the v-model is assessed every time the input event is fired (i.e. on key-down or paste). If you want to wait until the user has finished typing and blurred the input field (click outside of the input field), you can use the lazy modifier over the v-model. Use the code given below to proceed:
<input v-model.lazy="value" />
- In the
CustomSearchInput.vue
file, create a custom component that accept props likevalue
,optionsArr
andplaceholderValue
.
- Here is the template structure for the
CustomSearchInput
component :
<template>
<div class="container">
<input ref="inputField" v-model="inputVal" class="val-input" type="text" :placeholder="placeholderValue />
<!-- options Dropdown -->
<div class="data-options" v-if="filterOptionArray.length > 0" :class="{ 'd-none': openFlag }" >
<div ref="dataDiv" class="general-modal-scroll-bar">
<li
v-for="option in filterOptionArray"
:key="option"
class="item"
@click=" inputVal = option;
$emit('input', option); " >
{{ option }}
</li>
</div>
</div>
</div>
</template>
- Now, let’s declare
props
and reactivedata property
using the code snippet given below:
<script>
export default {
name: "SearchInput",
props: {
optionsArr: {
type: Array,
default: () => [],
},
value: {
type: String,
default: "",
},
placeholderValue: {
type: String,
default: "",
},
},
data() {
return {
inputVal: "",
openFlag: true,
};
},
...
...
</script >
Now we will move on to write the logic for the drop-down toggling which includes the
computed property
, and if the user clicks any option we have to close the drop-down. We will have to close the dropdown when the page loads or the user leaves the page. For this, we are using Vue.jslife cycle hooks
,mounted
andbeforeDestroy
. Use the code snippet given below to proceed:<script> ... ... mounted() { this.inputVal = this.value; // Closing the options panel on load of the component document.addEventListener("click", this.close); }, beforeDestroy() { // Closing the options panel on leaving the component document.removeEventListener("click", this.close); }, ... ...
Here is where the main logic comes in! We have to filter the array coming from
prop
depending upon the user's input. Run the following code snippet:
<script>
...
...
// this computed property is responsible for the fitering the array according input value
computed: {
filterOptionArray() {
if (this.inputVal.length === 0) {
return this.optionsArr;
} else {
return this.optionsArr.filter((option) => {
let temp = option.toLowerCase();
return temp.match(this.inputVal);
});
}
},
},
// keep a watch on inputVal if it's value change then we close the options Panel
watch: {
inputVal(newVal) {
this.openFlag = false;
},
},
...
...
</script>
We also have to close the dropdown if the user clicks outside the drop-down area which can be done by running the following code:
<script> ... ... methods: { // Options Panel close method close(e) { if ( e.target !== this.$refs.dataDiv && e.target !== this.$refs.inputField ) { this.openFlag = true; } }, }, }; </script>
On following the aforementioned steps, we will receive the
value
as a prop from the parent component. As per the Vue 2 design pattern, we were not allowed to manipulate the prop data directly. To resolve this, we will be creating a local data property calledinputVal
and move on to storing thevalue
props data ininputVal
, and bind theinputVal
to theinput field
.
- Now, we will be creating a computed property called the
filterOptionArray
, which is responsible for the options available to select from the dropdown depending upon the user input. This computed property returns an array, which we use to display the drop-down list.
- When a user clicks on any drop-down list item, it emits an event to inform its parent about the changes. Now the template for the parent component will be the
App.js
file. Run the following code:
<template>
<div id="app">
<CustomSearchInput
:optionsArr="arr"
v-model="selected"
placeholderValue="Enter start typing to get options"
/>
<div class="display-data" v-if="selected.length > 0" >Selected Value: {{ selected }}</div>
</div>
</template>
- Now, we have to import the child component and declare the required data values as shown below:
<script>
import CustomSearchInput from "./components/CoustomSearchInput";
export default {
name: "App",
components: { CustomSearchInput },
data() {
return {
arr: ["jhon", "jay", "shiv", "jani", "shawn", "shelly", "bob", "carry", "Alex", "Anthoney" ],
selected: "",
};
},
};
</script>
Play with the code here
- We can use this method to combine computed properties and the v-model in order to write the most robust code on Vue.js.
Add-on Notes:
- If you’d like to cast user input to a number instead of a string, add the
v-model.number
modifier.- The
v-model.trim
modifier will strip leading or trailing whitespace from the bound string. (It can not be used in conjunction with thev-model.number
).
Conclusion
The combination of the v-model and computed property makes your code robust and more reusable. Hope this article helps you to understand how to use two-way data binding in Vue.js by using the v-model directive and computed properties.