Goal
I want to detect only a change event to searchTerms
.
Problem
The watcher currently triggers on each keypress, but I don't want that many events.
Context (View Fiddle)
<template>
<div id="app">
<table class="table">
<tr>
<td><label>Name</label></td>
<td><input class="form-control" v-model="customer.name" autofocus></td>
</tr>
<tr>
<td><label>Short Code</label></td>
<td><input class="form-control" v-model="customer.shortCode"></td>
</tr>
<tr>
<td><label>Address</label></td>
<td><input class="form-control" v-model="customer.address"></td>
</tr>
<tr>
<td><label>Caller</label></td>
<td><input class="form-control" v-model="customer.caller"></td>
</tr>
<tr>
<td><label>Phone</label></td>
<td><input class="form-control" v-model="customer.phone"></td>
</tr>
</table>
<div class="models">
<pre><strong>customer:</strong> {{ customer | json }}</pre>
<pre><strong>searchTerms:</strong> {{ searchTerms | json }}</pre>
</div>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {
customer: {
name: 'Donnie',
phone: '',
caller: '',
address: '',
shortCode: 'DO'
}
},
puted: {
searchTerms: function() {
let terms = {};
_.forOwn(this.customer, (value, key) => {
if (value.length >= 3) {
terms[key] = value;
}
});
return terms;
}
},
watch: {
'searchTerms': function() {
if (_.isEmpty(this.searchTerms)) {
return;
}
alert('searchTerms Changed');
}
}
});
</script>
Goal
I want to detect only a change event to searchTerms
.
Problem
The watcher currently triggers on each keypress, but I don't want that many events.
Context (View Fiddle)
<template>
<div id="app">
<table class="table">
<tr>
<td><label>Name</label></td>
<td><input class="form-control" v-model="customer.name" autofocus></td>
</tr>
<tr>
<td><label>Short Code</label></td>
<td><input class="form-control" v-model="customer.shortCode"></td>
</tr>
<tr>
<td><label>Address</label></td>
<td><input class="form-control" v-model="customer.address"></td>
</tr>
<tr>
<td><label>Caller</label></td>
<td><input class="form-control" v-model="customer.caller"></td>
</tr>
<tr>
<td><label>Phone</label></td>
<td><input class="form-control" v-model="customer.phone"></td>
</tr>
</table>
<div class="models">
<pre><strong>customer:</strong> {{ customer | json }}</pre>
<pre><strong>searchTerms:</strong> {{ searchTerms | json }}</pre>
</div>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {
customer: {
name: 'Donnie',
phone: '',
caller: '',
address: '',
shortCode: 'DO'
}
},
puted: {
searchTerms: function() {
let terms = {};
_.forOwn(this.customer, (value, key) => {
if (value.length >= 3) {
terms[key] = value;
}
});
return terms;
}
},
watch: {
'searchTerms': function() {
if (_.isEmpty(this.searchTerms)) {
return;
}
alert('searchTerms Changed');
}
}
});
</script>
Share
Improve this question
edited Dec 2, 2016 at 16:25
Donnie
asked Dec 2, 2016 at 15:08
DonnieDonnie
6,35110 gold badges38 silver badges55 bronze badges
3 Answers
Reset to default 0The puted property searchTerms
creates a new object every time it runs. This means that the reference to searchTerms
changes, causing the watcher to fire.
You only want the watcher to fire if one of the values has changed. The easiest way to do this is to watch a stringified version of searchTerms
, rather than the object.
Here is the updated fiddle: https://jsfiddle/qLzu0seq/5/
And here is the code as a snippet (it's nice to keep code in stackoverflow, rather than an external site):
new Vue({
el: '#app',
data: {
customer: {
name: 'Donnie',
phone: '',
caller: '',
address: '',
shortCode: 'DO'
}
},
puted: {
searchTerms: function() {
let terms = {};
_.forOwn(this.customer, (value, key) => {
if (value.length >= 3) {
terms[key] = value;
}
});
return terms;
},
searchTermsStringified: function() {
return JSON.stringify(this.searchTerms);
}
},
watch: {
'searchTermsStringified': function() {
if (_.isEmpty(this.searchTerms)) {
return;
}
alert('searchTerms Changed');
}
}
});
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.1.4/vue.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/lodash.js/4.17.2/lodash.js"></script>
<div id="app">
<table class="table">
<tr>
<td><label>Name</label></td>
<td><input class="form-control" v-model="customer.name" autofocus></td>
</tr>
<tr>
<td><label>Short Code</label></td>
<td><input class="form-control" v-model="customer.shortCode"></td>
</tr>
<tr>
<td><label>Address</label></td>
<td><input class="form-control" v-model="customer.address"></td>
</tr>
<tr>
<td><label>Caller</label></td>
<td><input class="form-control" v-model="customer.caller"></td>
</tr>
<tr>
<td><label>Phone</label></td>
<td><input class="form-control" v-model="customer.phone"></td>
</tr>
</table>
<div class="models">
<pre><strong>customer:</strong> {{ JSON.stringify(customer,null,2) }}</pre>
<pre><strong>searchTerms:</strong> {{ JSON.stringify(searchTerms,null,2) }}</pre>
</div>
</div>
You can check if the value has changed directly in the puted property function.
Since you are generating objects you need to use _.isEqual
method in order to test if the value has changed. You will also need to store the previous value for parison.
new Vue({
el: '#app',
data: {
customer: {
name: 'Donnie',
phone: '',
caller: '',
address: '',
shortCode: 'DO'
},
previousSearchTerms: null
},
puted: {
searchTerms: function() {
let terms = {};
_.forOwn(this.customer, (value, key) => {
if (value.length >= 3) {
terms[key] = value;
}
});
if (this.previousSearchTerms && !_.isEqual(terms, this.previousSearchTerms)) {
alert('I was changed !');
}
this.previousSearchTerms = terms;
return terms;
}
}
});
label { font-weight: bold; }
.models {
background: #eee;
margin: 1rem;
padding: 1rem;
}
<script src="https://cdnjs.cloudflare./ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare./ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" />
<div id="app">
<table class="table">
<tr>
<td><label>Name</label></td>
<td><input class="form-control" v-model="customer.name" autofocus></td>
</tr>
<tr>
<td><label>Short Code</label></td>
<td><input class="form-control" v-model="customer.shortCode"></td>
</tr>
<tr>
<td><label>Address</label></td>
<td><input class="form-control" v-model="customer.address"></td>
</tr>
<tr>
<td><label>Caller</label></td>
<td><input class="form-control" v-model="customer.caller"></td>
</tr>
<tr>
<td><label>Phone</label></td>
<td><input class="form-control" v-model="customer.phone"></td>
</tr>
</table>
<div class="models">
<pre><strong>customer:</strong> {{ customer | json }}</pre>
<pre><strong>searchTerms:</strong> {{ searchTerms | json }}</pre>
</div>
</div>
<script src="//cdnjs.cloudflare./ajax/libs/vue/1.0.12/vue.js"></script>
You can use debounce for this, which is provided by lodash. It Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked. Debouncing is used to limit how often we execute Ajax requests and other expensive operations
You can add the things you want to not call that often in a separate method, and call those actions inside a _.debounce
, like following:
methods: {
// This is where the debounce actually belongs.
expensiveOperation: _.debounce(function () {
this.isCalculating = true
setTimeout(function () {
alert('searchTerms Changed');
}.bind(this), 1000)
}, 500)
}
You can change the delay in setTimeout
as par your requirement.
Find updated fiddle here.