How to solve vue bind by methods like this - vue.js

Look.
When data_a changes, getDataB will execute.
How to deal with this problem?
<template>
<div>
<div :data-a="data_a">
demo show params A
</div>
<div :data-b="getDataB()">
demo show params B
</div>
</div>
</template>
<script>
export default {
data () {
return {
data_a: 0,
datas: [
0, 1, 2, 3, 4
]
}
},
methods: {
getDataB () {
console.log('getDataB() called');
}
},
mounted () {
setInterval(function () {
this.data_a = parseInt(Math.floor(Math.random() * 10000000000));
}.bind(this), 1000);
}
}
</script>

First of all - switch to ES6 and avoid bind.
mounted () {
setInterval( () => {
this.data_a = parseInt(Math.floor(Math.random() * 10000000000));
}, 1000);
}
Then modify getDataB to make it computed:
computed: {
dataB () {
// do something with data_a
console.log('calc based on this.data_a in progress...')
return this.data_a*2 // ;)
}
},

Related

How to react data changes in vue component?

I am trying to build a component that should update the string using setInterval.
Here is the code of the component.
<script>
export default {
data() {
return {
offers: [
'1!',
'2!',
'3!',
],
current_index: 0,
indexInterval: null,
}
},
created() {
this.updateToastIndex()
},
beforeDestroy() {
clearInterval(this.indexInterval)
},
methods: {
updateToastIndex() {
const indexInterval = setInterval(() => {
this.$nextTick(() => {
if (this.current_index === 2) {
this.current_index = 0
console.log(this.offers[this.current_index], 'changedprev')
} else {
this.current_index = this.current_index + 1
console.log(this.offers[this.current_index], 'changed')
}
})
}, 1000)
this.indexInterval = indexInterval
},
},
}
</script>
<template>
<div>{{ offers[current_index] }}</div>
</template>
I can see the current_index in updateToastIndex function getting updated but current_index in template is not getting updated.
Please suggest some solution

Nuxt + Vuex mapGetters value is always undefined

I'm using VueX with Nuxt.JS so let's suppose the following code in the file store/search.js:
export const state = () => ({
results: null
});
export const mutations = {
setResults(state, { results }) {
state.results = results;
}
};
export const actions = {
startSearch({ commit, dispatch }, { type, filters }) {
commit("setResults", { type, filters });
}
};
export const getters = {
results: state => state.results
};
Now in my component results.vue, under the computed property I have something like this:
<template>
<button #click="handleSearch">Search</button>
<div v-if="results && results.length" class="results" >
<div v-for="item in results" :key="item.id">
{{item}}
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
data() {
return {
selected_type: null,
filters: null
};
},
methods: {
setType(type) {
this.selected_type = type;
this.handleSearch();
},
setFilters(filters) {
this.filters = filters;
},
handleSearch() {
this.startSearch({ type: this.selected_type, filters: this.filters });
},
...mapActions("search", {
startSearch: "startSearch"
})
},
computed: {
...mapGetters("search", {
results: "results"
})
}
</script>
My question is: why the item in the for loop (in the template section) always return undefined ?
Thank you very much for your answers.
So far, I found it:
in computed should be an array, not an object so:
...mapGetters("search", [
"results"
]
// Now results is populated.

VueJS input with v-model breaks the user input [duplicate]

I have a simple input box in a Vue template and I would like to use debounce more or less like this:
<input type="text" v-model="filterKey" debounce="500">
However the debounce property has been deprecated in Vue 2. The recommendation only says: "use v-on:input + 3rd party debounce function".
How do you correctly implement it?
I've tried to implement it using lodash, v-on:input and v-model, but I am wondering if it is possible to do without the extra variable.
In template:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
In script:
data: function () {
return {
searchInput: '',
filterKey: ''
}
},
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
The filterkey is then used later in computed props.
I am using debounce NPM package and implemented like this:
<input #input="debounceInput">
methods: {
debounceInput: debounce(function (e) {
this.$store.dispatch('updateInput', e.target.value)
}, config.debouncers.default)
}
Using lodash and the example in the question, the implementation looks like this:
<input v-on:input="debounceInput">
methods: {
debounceInput: _.debounce(function (e) {
this.filterKey = e.target.value;
}, 500)
}
Option 1: Re-usable, no deps
- Recommended if needed more than once in your project
/helpers.js
export function debounce (fn, delay) {
var timeoutID = null
return function () {
clearTimeout(timeoutID)
var args = arguments
var that = this
timeoutID = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}
Typescript?
export function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
let timeoutID: number | null = null;
return function (this: any, ...args: any[]) {
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
fn.apply(this, args);
}, delay);
} as T;
}
Or if using a d.ts:
declare function debounce(fn: (...args: any[]) => void, delay: number): (...args: any[]) => void;
/Component.vue
<script>
import {debounce} from './helpers'
export default {
data () {
return {
input: '',
debouncedInput: ''
}
},
watch: {
input: debounce(function (newVal) {
this.debouncedInput = newVal
}, 500)
}
}
</script>
Codepen
Option 2: In-component, also no deps
- Recommended if using once or in small project
/Component.vue
<template>
<input type="text" v-model="input" />
</template>
<script>
export default {
data: {
timeout: null,
debouncedInput: ''
},
computed: {
input: {
get() {
return this.debouncedInput
},
set(val) {
if (this.timeout) clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.debouncedInput = val
}, 300)
}
}
}
}
</script>
Codepen
Assigning debounce in methods can be trouble. So instead of this:
// Bad
methods: {
foo: _.debounce(function(){}, 1000)
}
You may try:
// Good
created () {
this.foo = _.debounce(function(){}, 1000);
}
It becomes an issue if you have multiple instances of a component - similar to the way data should be a function that returns an object. Each instance needs its own debounce function if they are supposed to act independently.
Here's an example of the problem:
Vue.component('counter', {
template: '<div>{{ i }}</div>',
data: function(){
return { i: 0 };
},
methods: {
// DON'T DO THIS
increment: _.debounce(function(){
this.i += 1;
}, 1000)
}
});
new Vue({
el: '#app',
mounted () {
this.$refs.counter1.increment();
this.$refs.counter2.increment();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<div id="app">
<div>Both should change from 0 to 1:</div>
<counter ref="counter1"></counter>
<counter ref="counter2"></counter>
</div>
Very simple without lodash
handleScroll: function() {
if (this.timeout)
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
// your action
}, 200); // delay
}
I had the same problem and here is a solution that works without plugins.
Since <input v-model="xxxx"> is exactly the same as
<input
v-bind:value="xxxx"
v-on:input="xxxx = $event.target.value"
>
(source)
I figured I could set a debounce function on the assigning of xxxx in xxxx = $event.target.value
like this
<input
v-bind:value="xxxx"
v-on:input="debounceSearch($event.target.value)"
>
methods:
debounceSearch(val){
if(search_timeout) clearTimeout(search_timeout);
var that=this;
search_timeout = setTimeout(function() {
that.xxxx = val;
}, 400);
},
If you need a very minimalistic approach to this, I made one (originally forked from vuejs-tips to also support IE) which is available here: https://www.npmjs.com/package/v-debounce
Usage:
<input v-model.lazy="term" v-debounce="delay" placeholder="Search for something" />
Then in your component:
<script>
export default {
name: 'example',
data () {
return {
delay: 1000,
term: '',
}
},
watch: {
term () {
// Do something with search term after it debounced
console.log(`Search term changed to ${this.term}`)
}
},
directives: {
debounce
}
}
</script>
Please note that I posted this answer before the accepted answer. It's not
correct. It's just a step forward from the solution in the
question. I have edited the accepted question to show both the author's implementation and the final implementation I had used.
Based on comments and the linked migration document, I've made a few changes to the code:
In template:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
In script:
watch: {
searchInput: function () {
this.debounceInput();
}
},
And the method that sets the filter key stays the same:
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
This looks like there is one less call (just the v-model, and not the v-on:input).
In case you need to apply a dynamic delay with the lodash's debounce function:
props: {
delay: String
},
data: () => ({
search: null
}),
created () {
this.valueChanged = debounce(function (event) {
// Here you have access to `this`
this.makeAPIrequest(event.target.value)
}.bind(this), this.delay)
},
methods: {
makeAPIrequest (newVal) {
// ...
}
}
And the template:
<template>
//...
<input type="text" v-model="search" #input="valueChanged" />
//...
</template>
NOTE: in the example above I made an example of search input which can call the API with a custom delay which is provided in props
Although pretty much all answers here are already correct, if anyone is in search of a quick solution I have a directive for this.
https://www.npmjs.com/package/vue-lazy-input
It applies to #input and v-model, supports custom components and DOM elements, debounce and throttle.
Vue.use(VueLazyInput)
new Vue({
el: '#app',
data() {
return {
val: 42
}
},
methods:{
onLazyInput(e){
console.log(e.target.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/lodash/lodash.min.js"></script><!-- dependency -->
<script src="https://unpkg.com/vue-lazy-input#latest"></script>
<div id="app">
<input type="range" v-model="val" #input="onLazyInput" v-lazy-input /> {{val}}
</div>
To create debounced methods you can use computeds, that way they won't be shared across multiple instances of your component:
<template>
<input #input="handleInputDebounced">
<template>
<script>
import debounce from 'lodash.debouce';
export default {
props: {
timeout: {
type: Number,
default: 200,
},
},
methods: {
handleInput(event) {
// input handling logic
},
},
computed: {
handleInputDebounced() {
return debounce(this.handleInput, this.timeout);
},
},
}
</script>
You can make it work with uncontrolled v-model as well:
<template>
<input v-model="debouncedModel">
<template>
<script>
import debounce from 'lodash.debouce';
export default {
props: {
value: String,
timeout: {
type: Number,
default: 200,
},
},
methods: {
updateValue(value) {
this.$emit('input', value);
},
},
computed: {
updateValueDebounced() {
return debounce(this.updateValue, this.timeout);
},
debouncedModel: {
get() { return this.value; },
set(value) { this.updateValueDebounced(value); }
},
},
}
</script>
Here is a vue3 way
...
<input v-model="searchInput">
...
setup(){
const searchInput = ref(null)
const timeoutID = ref(null)
watch(searchInput, (new, old) => {
clearTimeout(timeoutID.value)
timeoutID.value = setTimeout(() => {
//Call function for searching
}, 500) //millisecons before it is run
})
return {...}
}
If you are using Vue you can also use v.model.lazy instead of debounce but remember v.model.lazy will not always work as Vue limits it for custom components.
For custom components you should use :value along with #change.native
<b-input :value="data" #change.native="data = $event.target.value" ></b-input>
1 Short version using arrow function, with default delay value
file: debounce.js in ex: ( import debounce from '../../utils/debounce' )
export default function (callback, delay=300) {
let timeout = null
return (...args) => {
clearTimeout(timeout)
const context = this
timeout = setTimeout(() => callback.apply(context, args), delay)
}
}
2 Mixin option
file: debounceMixin.js
export default {
methods: {
debounce(func, delay=300) {
let debounceTimer;
return function() {
// console.log("debouncing call..");
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
// console.log("..done");
};
}
}
};
Use in vueComponent:
<script>
import debounceMixin from "../mixins/debounceMixin";
export default {
mixins: [debounceMixin],
data() {
return {
isUserIdValid: false,
};
},
mounted() {
this.isUserIdValid = this.debounce(this.checkUserIdValid, 1000);
},
methods: {
isUserIdValid(id){
// logic
}
}
</script>
another option, example
Vue search input debounce
Here's an example Vue 2 component that demonstrates how to use debounce.
<template>
<div>
<v-btn #click="properDebounceMyMethod">Proper debounce</v-btn>
<v-btn #click="notWorkingDebounceMyMethod">!debounce</v-btn>
<v-btn #click="myMethod">normal call</v-btn>
</div>
</template>
<script lang="ts" >
import { defineComponent } from '#vue/composition-api';
import { debounce } from 'lodash';
export default defineComponent({
name: 'DebounceExample',
created() {
// debounce instance method dynamically on created hook
this.properDebounceMyMethod = debounce(this.properDebounceMyMethod, 500);
},
methods: {
properDebounceMyMethod(){
this.myMethod();
},
notWorkingDebounceMyMethod() {
debounce(this.myMethod, 500);
},
myMethod() {
console.log('hi from my method');
},
}
});
</script>
If you could move the execution of the debounce function into some class method you could use a decorator from the utils-decorators lib (npm install --save utils-decorators):
import {debounce} from 'utils-decorators';
class SomeService {
#debounce(500)
getData(params) {
}
}
I was able to use debounce with very little implementation.
I am using Vue 2.6.14 with boostrap-vue:
Add this pkg to your package.json: https://www.npmjs.com/package/debounce
Add this to main.js:
import { debounce } from "debounce";
Vue.use(debounce);
In my component I have this input:
<b-form-input
debounce="600"
#update="search()"
trim
id="username"
v-model="form.userName"
type="text"
placeholder="Enter username"
required
>
</b-form-input>
All it does is call the search() method and the search method uses the form.userName for perform the search.
<template>
<input type="text" v-model="search" #input="debouncedSearch" />
</template>
<script>
import _ from 'lodash';
export default {
data() {
return {
search: '',
};
},
methods: {
search() {
// Perform the search here
console.log(this.search);
},
},
created() {
this.debouncedSearch = _.debounce(this.search, 1000);
},
};
</script>
public debChannel = debounce((key) => this.remoteMethodChannelName(key), 200)
vue-property-decorator

listen to events from dynamic vue components

How would you listen to an event emitted by a dynamically created component instance?
In the example, the top component is added in the DOM, while the second is dynamically created in javascript.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
}
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
button_counter_instance.$on('myevent', function(count) {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
If I've understood what you want then you just need to listen for the event on the inner component and pass it on.
I've used arrow functions in a couple of places to avoid problems with this bindings. Otherwise I've tried to leave your code unchanged as much as possible. Changes marked with ****.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
},
// **** Added this ****
on: {
myevent: count => {
this.$emit('myevent', count);
}
}
// ****
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
// **** Changed the next line ****
button_counter_instance.$on('myevent', count => {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
It's important to understand that button_counter_instance is not an instance of your button-counter component. You've wrapped it in another component, albeit a component that doesn't add any extra DOM nodes. So listening on the wrapper component is not the same as listening on button-counter.
Docs for what you can pass to h: https://v2.vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth

How to implement debounce in Vue2?

I have a simple input box in a Vue template and I would like to use debounce more or less like this:
<input type="text" v-model="filterKey" debounce="500">
However the debounce property has been deprecated in Vue 2. The recommendation only says: "use v-on:input + 3rd party debounce function".
How do you correctly implement it?
I've tried to implement it using lodash, v-on:input and v-model, but I am wondering if it is possible to do without the extra variable.
In template:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
In script:
data: function () {
return {
searchInput: '',
filterKey: ''
}
},
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
The filterkey is then used later in computed props.
I am using debounce NPM package and implemented like this:
<input #input="debounceInput">
methods: {
debounceInput: debounce(function (e) {
this.$store.dispatch('updateInput', e.target.value)
}, config.debouncers.default)
}
Using lodash and the example in the question, the implementation looks like this:
<input v-on:input="debounceInput">
methods: {
debounceInput: _.debounce(function (e) {
this.filterKey = e.target.value;
}, 500)
}
Option 1: Re-usable, no deps
- Recommended if needed more than once in your project
/helpers.js
export function debounce (fn, delay) {
var timeoutID = null
return function () {
clearTimeout(timeoutID)
var args = arguments
var that = this
timeoutID = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}
Typescript?
export function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
let timeoutID: number | null = null;
return function (this: any, ...args: any[]) {
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
fn.apply(this, args);
}, delay);
} as T;
}
Or if using a d.ts:
declare function debounce(fn: (...args: any[]) => void, delay: number): (...args: any[]) => void;
/Component.vue
<script>
import {debounce} from './helpers'
export default {
data () {
return {
input: '',
debouncedInput: ''
}
},
watch: {
input: debounce(function (newVal) {
this.debouncedInput = newVal
}, 500)
}
}
</script>
Codepen
Option 2: In-component, also no deps
- Recommended if using once or in small project
/Component.vue
<template>
<input type="text" v-model="input" />
</template>
<script>
export default {
data: {
timeout: null,
debouncedInput: ''
},
computed: {
input: {
get() {
return this.debouncedInput
},
set(val) {
if (this.timeout) clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.debouncedInput = val
}, 300)
}
}
}
}
</script>
Codepen
Assigning debounce in methods can be trouble. So instead of this:
// Bad
methods: {
foo: _.debounce(function(){}, 1000)
}
You may try:
// Good
created () {
this.foo = _.debounce(function(){}, 1000);
}
It becomes an issue if you have multiple instances of a component - similar to the way data should be a function that returns an object. Each instance needs its own debounce function if they are supposed to act independently.
Here's an example of the problem:
Vue.component('counter', {
template: '<div>{{ i }}</div>',
data: function(){
return { i: 0 };
},
methods: {
// DON'T DO THIS
increment: _.debounce(function(){
this.i += 1;
}, 1000)
}
});
new Vue({
el: '#app',
mounted () {
this.$refs.counter1.increment();
this.$refs.counter2.increment();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<div id="app">
<div>Both should change from 0 to 1:</div>
<counter ref="counter1"></counter>
<counter ref="counter2"></counter>
</div>
Very simple without lodash
handleScroll: function() {
if (this.timeout)
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
// your action
}, 200); // delay
}
I had the same problem and here is a solution that works without plugins.
Since <input v-model="xxxx"> is exactly the same as
<input
v-bind:value="xxxx"
v-on:input="xxxx = $event.target.value"
>
(source)
I figured I could set a debounce function on the assigning of xxxx in xxxx = $event.target.value
like this
<input
v-bind:value="xxxx"
v-on:input="debounceSearch($event.target.value)"
>
methods:
debounceSearch(val){
if(search_timeout) clearTimeout(search_timeout);
var that=this;
search_timeout = setTimeout(function() {
that.xxxx = val;
}, 400);
},
If you need a very minimalistic approach to this, I made one (originally forked from vuejs-tips to also support IE) which is available here: https://www.npmjs.com/package/v-debounce
Usage:
<input v-model.lazy="term" v-debounce="delay" placeholder="Search for something" />
Then in your component:
<script>
export default {
name: 'example',
data () {
return {
delay: 1000,
term: '',
}
},
watch: {
term () {
// Do something with search term after it debounced
console.log(`Search term changed to ${this.term}`)
}
},
directives: {
debounce
}
}
</script>
Please note that I posted this answer before the accepted answer. It's not
correct. It's just a step forward from the solution in the
question. I have edited the accepted question to show both the author's implementation and the final implementation I had used.
Based on comments and the linked migration document, I've made a few changes to the code:
In template:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
In script:
watch: {
searchInput: function () {
this.debounceInput();
}
},
And the method that sets the filter key stays the same:
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
This looks like there is one less call (just the v-model, and not the v-on:input).
In case you need to apply a dynamic delay with the lodash's debounce function:
props: {
delay: String
},
data: () => ({
search: null
}),
created () {
this.valueChanged = debounce(function (event) {
// Here you have access to `this`
this.makeAPIrequest(event.target.value)
}.bind(this), this.delay)
},
methods: {
makeAPIrequest (newVal) {
// ...
}
}
And the template:
<template>
//...
<input type="text" v-model="search" #input="valueChanged" />
//...
</template>
NOTE: in the example above I made an example of search input which can call the API with a custom delay which is provided in props
Although pretty much all answers here are already correct, if anyone is in search of a quick solution I have a directive for this.
https://www.npmjs.com/package/vue-lazy-input
It applies to #input and v-model, supports custom components and DOM elements, debounce and throttle.
Vue.use(VueLazyInput)
new Vue({
el: '#app',
data() {
return {
val: 42
}
},
methods:{
onLazyInput(e){
console.log(e.target.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/lodash/lodash.min.js"></script><!-- dependency -->
<script src="https://unpkg.com/vue-lazy-input#latest"></script>
<div id="app">
<input type="range" v-model="val" #input="onLazyInput" v-lazy-input /> {{val}}
</div>
To create debounced methods you can use computeds, that way they won't be shared across multiple instances of your component:
<template>
<input #input="handleInputDebounced">
<template>
<script>
import debounce from 'lodash.debouce';
export default {
props: {
timeout: {
type: Number,
default: 200,
},
},
methods: {
handleInput(event) {
// input handling logic
},
},
computed: {
handleInputDebounced() {
return debounce(this.handleInput, this.timeout);
},
},
}
</script>
You can make it work with uncontrolled v-model as well:
<template>
<input v-model="debouncedModel">
<template>
<script>
import debounce from 'lodash.debouce';
export default {
props: {
value: String,
timeout: {
type: Number,
default: 200,
},
},
methods: {
updateValue(value) {
this.$emit('input', value);
},
},
computed: {
updateValueDebounced() {
return debounce(this.updateValue, this.timeout);
},
debouncedModel: {
get() { return this.value; },
set(value) { this.updateValueDebounced(value); }
},
},
}
</script>
Here is a vue3 way
...
<input v-model="searchInput">
...
setup(){
const searchInput = ref(null)
const timeoutID = ref(null)
watch(searchInput, (new, old) => {
clearTimeout(timeoutID.value)
timeoutID.value = setTimeout(() => {
//Call function for searching
}, 500) //millisecons before it is run
})
return {...}
}
If you are using Vue you can also use v.model.lazy instead of debounce but remember v.model.lazy will not always work as Vue limits it for custom components.
For custom components you should use :value along with #change.native
<b-input :value="data" #change.native="data = $event.target.value" ></b-input>
1 Short version using arrow function, with default delay value
file: debounce.js in ex: ( import debounce from '../../utils/debounce' )
export default function (callback, delay=300) {
let timeout = null
return (...args) => {
clearTimeout(timeout)
const context = this
timeout = setTimeout(() => callback.apply(context, args), delay)
}
}
2 Mixin option
file: debounceMixin.js
export default {
methods: {
debounce(func, delay=300) {
let debounceTimer;
return function() {
// console.log("debouncing call..");
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
// console.log("..done");
};
}
}
};
Use in vueComponent:
<script>
import debounceMixin from "../mixins/debounceMixin";
export default {
mixins: [debounceMixin],
data() {
return {
isUserIdValid: false,
};
},
mounted() {
this.isUserIdValid = this.debounce(this.checkUserIdValid, 1000);
},
methods: {
isUserIdValid(id){
// logic
}
}
</script>
another option, example
Vue search input debounce
Here's an example Vue 2 component that demonstrates how to use debounce.
<template>
<div>
<v-btn #click="properDebounceMyMethod">Proper debounce</v-btn>
<v-btn #click="notWorkingDebounceMyMethod">!debounce</v-btn>
<v-btn #click="myMethod">normal call</v-btn>
</div>
</template>
<script lang="ts" >
import { defineComponent } from '#vue/composition-api';
import { debounce } from 'lodash';
export default defineComponent({
name: 'DebounceExample',
created() {
// debounce instance method dynamically on created hook
this.properDebounceMyMethod = debounce(this.properDebounceMyMethod, 500);
},
methods: {
properDebounceMyMethod(){
this.myMethod();
},
notWorkingDebounceMyMethod() {
debounce(this.myMethod, 500);
},
myMethod() {
console.log('hi from my method');
},
}
});
</script>
If you could move the execution of the debounce function into some class method you could use a decorator from the utils-decorators lib (npm install --save utils-decorators):
import {debounce} from 'utils-decorators';
class SomeService {
#debounce(500)
getData(params) {
}
}
I was able to use debounce with very little implementation.
I am using Vue 2.6.14 with boostrap-vue:
Add this pkg to your package.json: https://www.npmjs.com/package/debounce
Add this to main.js:
import { debounce } from "debounce";
Vue.use(debounce);
In my component I have this input:
<b-form-input
debounce="600"
#update="search()"
trim
id="username"
v-model="form.userName"
type="text"
placeholder="Enter username"
required
>
</b-form-input>
All it does is call the search() method and the search method uses the form.userName for perform the search.
<template>
<input type="text" v-model="search" #input="debouncedSearch" />
</template>
<script>
import _ from 'lodash';
export default {
data() {
return {
search: '',
};
},
methods: {
search() {
// Perform the search here
console.log(this.search);
},
},
created() {
this.debouncedSearch = _.debounce(this.search, 1000);
},
};
</script>
public debChannel = debounce((key) => this.remoteMethodChannelName(key), 200)
vue-property-decorator