spatie / vue-table-component

A straight to the point Vue component to display tables
http://vue-table-component.spatie.be
MIT License
588 stars 149 forks source link

Debounce filter data queries #94

Closed bryantbj closed 6 years ago

bryantbj commented 7 years ago

Issue

Has anyone devised a way to debounce the network requests when using remotely-fetched data? I would like the ability to set a debounce prop value to prevent numerous filter requests from being sent to the server immediately. Only after the user is done typing would the request be sent.

Current behavior:

User types filter text into the filter box. For each character typed, a request is immediately sent to the server, four total in the case of "open" being the text.

Desired behavior:

User types filter text into the filter box. For each character typed, a debounce prop (perhaps a number of milliseconds) is observed before sending the request. A request is not sent to the server until the time between characters typed has exceeded the debounce value.

Edit

I figured out a way to do this and I'll share my solution below in case someone else needs something similar.

<template>
  <table-component :data="fetchData"></table-component>
</template>

<script>
export default () {
  data () {
    return {
      debounce: false,
      debounceDuration: 500,
      debounceTimeout: null
    }
  },
  methods: {
    // This is passed as the data prop to the table component
    async fetchData (opts) {
      let ret = await this.fetchTimeout(opts)
      if (!this.debounce) { this.debounce = true }
      return ret
    },

    // This determines whether the request should be debounced
    //    and delays the request if necessary or invokes it immediately
    //    if debouncing is not necessary
    async fetchTimeout (opts) {
      return new Promise((resolve) => {
        if (this.debounce) {
          if (this.debounceTimeout) { clearTimeout(this.debounceTimeout) }
          this.debounceTimeout = setTimeout(() => {
            resolve(this.getRemoteData(opts))
          }, this.debounceDuration)
        } else {
          resolve(this.getRemoteData(opts))
        }
      })
    },

    // This performs a GET request for data
    async getRemoteData (opts) {
      // query params
      let filter = opts.filter
      let sort = opts.sort.order
      let page = opts.page
      // initiate the request
      const dataPromise = this.$http.get(`${uri}?filter=${filter}&sort=${sort}&page=${page}`)
      const resp = await dataPromise
      // format the return data
      const ret = {
        data: JSON.parse(resp.data.data),
        pagination: JSON.parse(resp.data.pagination)
      }
      return ret
    }
  }
}
</script>

It may not be the prettiest ever, but it gets the job done.

sebastiandedeyne commented 6 years ago

We're not going to add debouncing to the package for now. Your solution looks like the way to go for now, or if you don't want to maintain any debounce logic yourself, lodash has a great debounce higher order function.

https://lodash.com/docs/4.17.4#debounce

dadigu commented 6 years ago

@sebastiandedeyne Sorry for opening up this ticket again, but I see you suggested using lodash debounce for debouncing the fetchData method, I'm having hard time getting it to work, could you provide an example?

saqueib commented 6 years ago

@sebastiandedeyne same here, I cant make it work with lodash debounce

fetchData: _.debounce(async function(param) {
    let res = await axios.get(this.url );

    return { 
        data: res.data.data,
        pagination: res.data.pagination    
    }
});

fetchData() returns undefined, TypeError: Cannot read property 'pagination' of undefined

hrakotom commented 6 years ago

If still having issues, you need to tie the debounce to the input, not fetchData. updating the filter variable on debounce then triggers fetchdata.

in methods: debouncedFilter: debounce(function(value){ this.filter = value; }, 500)

and remove v-model and in its place:

v-on:input="debouncedFilter" :input='filter'

fetchData remains unchanged

jdfx commented 6 years ago

I needed quite a few modifications, ended up just including the project in my own - got the debounce working by doing the following in TableComponent.vue

        watch: {
            filter: _.debounce(function(){

                if (!this.usesLocalData) {
                    this.mapDataToRows();
                }

                this.saveState();

            }, 500),

        data() {
            if (this.usesLocalData) {
                this.mapDataToRows();
            }
        },
    },