1

Template:

<input @keyup="fetchData(0)" v-model="name">

Method:

  methods: {
    fetchData: _.debounce(function (value) {
      console.log('Argument is: ' + value)
      axios.get(globalConfig.ORDERS_URL, {
        params: {
          limit: this.perPage,
          offset: (this.currentPage - 1) * this.perPage,
          name: this.name,
          phone: this.phone,
          from_date: this.dateOne,
          to_date: this.dateTwo
        }
      })
        .then((resp) => {
          this.req = resp.data
          // console.log(resp)
        })
        .catch((err) => {
          console.log(err)
        })
    }, 500)
  }

Calling fetchData with argument 0 - fetchData(0), but it won't pass because i'm using _.debounce. Console log shows "undefined"

How to correctly pass an argument?

Alexander Kim
  • 17,304
  • 23
  • 100
  • 157

3 Answers3

3

Install debounce package.

Import it in your component:

import debounce from 'debounce'

Update template by removing 0 from fetchData:

<input @input="fetchData" v-model="name">

I changed the event to @input but it will work with @keyup too.

Then change your function to:

fetchData: debounce(e => {
  console.log('Input argument is: ' + e.target.value);
}, 500)

https://codesandbox.io/s/8x6rj4lnzl (App.vue)

dziraf
  • 3,607
  • 1
  • 16
  • 22
  • It's work but how to access data in arrow function? 'this' is not work inside that fetchData function. – Bcktr Sep 05 '21 at 01:28
  • You can also use usual function with context to get access to 'this'. Something like debounce (function(e){ this.$emit('inputChanged', e) }, 500) – Denis Feb 07 '22 at 11:55
3

The following code has not been checked, but I wanted to explain more about debounce and how "v-on" or "@" events in Vue work.

On your <input> tag you can use <input @input="fetchData" v-model="name"> as @dziraf explained, and do a bunch of extra work for the extra variable. OR ...

TO SEND ARGUMENTS, you simply have to explicitly include the "EVENT" variable first (in Vue.js events it's name is $event), then any you choose to add:

<input 
  @input="fetchData($event, 'Custom Values', customVariables)"
  v-model="name"
>

Now you have access to all variables sent to "your function" or the "debounced function" (which is simply a wrapped version of any function you send it):

methods: {
  fetchData: 
    _.debounce( 
      (e, customVariable1=undefined, customVariable2=undefined) => {
        console.log('Input argument is: ' + e.target.value);
      }, 500),
...

HINT: The "=undefined" in the 2 customVariables is used to make them optional.

WARNING: Be CAREFUL if you will implement multiple instances of this component on a single page, then using your debounce function in methods: is NOT recommended due to some encapsulation issues during some initial Vue Lifecycle hooks. Also I believe you cannot call the .cancel() method if needed when you some other user interaction and stop the function before the delay is over.

It's recommended to save the function to a variable in the created() lifecycle rather than a method::

created() {
  this.fetchData = _.debounce( 
    (e, customVariable1=undefined, customVariable2=undefined) => {
      console.log('Input argument is: ' + e.target.value);
    }, 500);
}

Now, you can call the .cancel() method to properly garbage collect:

beforeDestroy() {
  this.fetchData.cancel();
}

Or, have a "Cancel" button or call from another UI element:

<button @click="fetchData.cancel();">Cancel the Request</button>

You can try this code to see the minor differences in the encapsulation:

HTML

<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>Using debounce as a data() variable declared in created():</div>
  <create-change ref="counter1"></create-change>
  <create-change ref="counter2"></create-change>
  <create-change ref="counter3"></create-change>
  <create-change ref="counter4"></create-change>
  <br />
  <br />
  <div>Using debounce as a Method:</div>
  <method-change ref="counter5"></method-change>
  <method-change ref="counter6"></method-change>
  <method-change ref="counter7"></method-change>
  <method-change ref="counter8"></method-change>
</div>

JS

Vue.component('create-change', {

  template:
    `
      <button @click="changeContent($event,'any custom value')">
        Click to change my value: {{ i }}
      </button>
    `,

  data: function(){
    return { i: 0 };
  },

  created() {
   this.changeContent = _.debounce(function(e, val=0){
      if (val) { this.i = val } // or reference a method here
    }, 1000)
  },

});

Vue.component('method-change', {

  template: 
    `
      <button @click="changeContent($event,'any custom value')">
        Click to change my value: {{ i }}
      </button>
    `,

  data: function(){
    return { i: 0 };
  },

  methods: {

    // WARNING: Can have encapsulation issues
   changeContent: _.debounce(function(e, val=0){
      if (val) { this.i = val }
    }, 1000),

  },

});


new Vue({
  el: '#app',
  mounted () {
    this.$refs.counter1.changeContent(null,1);
    this.$refs.counter2.changeContent(null,2);
    this.$refs.counter3.changeContent(null,3);
    this.$refs.counter4.changeContent(null,4);
    this.$refs.counter5.changeContent(null,5);
    this.$refs.counter6.changeContent(null,6);
    this.$refs.counter7.changeContent(null,7);
    this.$refs.counter8.changeContent(null,8);
  }
});

CSS

#app {
  margin: 20px;
  font-size: 20px;
  font-family: Arial, sans-serif;  
}
2

I was also looking for a solution, in general, here's a working version, I hope it will help save a minute to someone :)

// Lodash or any custom function debounce
import _ from 'lodash';

const template = `
   <input v-model="textInput">
`;

export default {
    template: template,
    data: () => {
        return {
            textInput: null
        }
    },
    created: function() {
        let vue = this;

        vue.debouncedApiPatch = _.debounce(vue.apiPatch, 500);

        vue.$watch('textInput', function (val) {
            vue.debouncedApiPatch('textInput', val);
        })
    },
    methods: {
        apiPatch: function (prop, val) {
            console.log(prop, val);
            // api async patch...
        }
    }
}
Naskalin
  • 895
  • 9
  • 9
  • This is the correct way according to the [official vue forum](https://forum.vuejs.org/t/lodash-debounce-not-working-when-placed-inside-a-method/86334/5) – George Marios Feb 25 '21 at 17:59