Search code examples
javascriptwordpressvue.jswordpress-rest-api

Problem with Vue.js when it needs to fetch an url (wp-api) on a keyup event


Here's my issue. I created a tool with vue.js and the WordPress API to search through the search endpoints for any keyword and display the result. So far so good, everything is working, except for a bug that I spotted.

Here's the deal:

const websiteurl = 'https://www.aaps.ca'; //yourwebsite or anything really
var vm = new Vue({
    el: '#blog-page',
    data: {
        noData: false,
        blogs: [],
        page: 0,
        search: '',
        totalPagesFetch: "",
        pageAmp: "&page=",
        apiURL: `${websiteurl}/wp-json/wp/v2/posts?per_page=6`,
        searchbyid: `${websiteurl}/wp-json/wp/v2/posts?per_page=6&include=`,
        searchUrl: `${websiteurl}/wp-json/wp/v2/search?subtype=post&per_page=6&search=`,
    },
    created: function () {
        this.fetchblogs();
    },
    methods: {
        fetchblogs: function () {
            let self = this;
            self.page = 1;
            let url = self.apiURL;
            fetch(url)
            .then(response => response.json())
            .then(data => vm.blogs = data);
        },
        searchonurl: function () {
            let ampersand = "&page=";
            searchPagination(1, this, ampersand);
        },
    }
});

function searchPagination(page, vm, pagen) {

    let self = vm;
    let searchword = self.search.toLowerCase();
    let newsearchbyid = self.searchbyid;
    let url;

    self.page = page;

    url = self.searchUrl + searchword + pagen + self.page;

    self.mycat = 'init';

    fetch(url)
    .then(response => {

        self.totalPagesFetch = response.headers.get("X-WP-TotalPages");
        return response.json();
    })
    .then(data => {
        
        let newid = [];
        data.forEach(function (item, index) {
            newid.push( item.id );
        });
        if (newid.length == 0) {
            return newsearchbyid + '0';
        } else {
            return newsearchbyid + newid;
        }
    })
    .then(response2 => {
        return fetch(response2)
    })
    .then(function(data2) {
        return data2.json();
    })
    .then(function(response3) {
        console.log(response3)
        if (response3.length == 0) {
            vm.noData = true;
            vm.blogs = response3;
        } else {
            vm.noData = false;
            vm.blogs = response3;
        }
    })
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="lazyblock-blogs testblog" id="blog-page">
    <div class="container">
        <div class="row controls">
            <div class="col-md-12">
                <div class="search-blog">
                    <img height="13" src="" alt="search">
                    <input id="sb" type="text" v-model="search" @keyup="searchonurl" placeholder="search">
                </div>
            </div>
        </div>

        <div class="row">

            <div class="col-md-4" v-for="(b, index) in blogs">
                <div class="h-100 box" v-cloak>
                    <img width="100%" v-bind:src=b.featured_image_url>
                    <a v-bind:href="b.link">
                        <h3 v-html=b.title.rendered></h3>
                    </a>
                    <div v-html=b.excerpt.rendered></div>
                    <p class="read-more"><a v-bind:href="b.link">read more</a></p>
                </div>
            </div>

            <div class="no-data" v-if="noData">
                <div class="h-100">
                    No post
                </div>
            </div>

        </div>

    </div>
</div>

I'm using a keyup event which is causing me some problems because it works, but in same cases, for example, if the user is very fast to type characters and then suddenly he wants to delete the word and start again, the response for the API has some sort of lag.

The problem is that I guess that the Vue framework is very responsive (I create a variable call search that will update immediately) but the API call in the network is not (please check my image here):

enter image description here

This first image appears if I type lll very fast, the third result will return nothing so it is an empty array, but if I will delete it immediately, it will return an url like that: https://www.aaps.ca//wp-json/wp/v2/search?subtype=post&per_page=6&search=&page=1 which in turn should return 6 results (as a default status).

The problem is that the network request won't return the last request but it gets crazy, it flashs and most of the time it returns the previous request (it is also very slow).

Is that a way to fix that?

I tried the delay function:

function sleeper(ms) {
  return function(x) {
    return new Promise(resolve => setTimeout(() => resolve(x), ms));
  };
}

and then I put before the then function:

.then(sleeper(1000))

but the result is the same, delayed by one second (for example)

Any thought?


Solution

  • This is the case for debounced function. Any existing implementation can be used, e.g. Lodash debounce. It needs to be declared once per component instance, i.e. in some lifecycle hook.

    That searchPagination accepts this as an argument means that something went wrong with its signature. Since it operates on component instance, it can be just a method and receive correct this context:

    methods: {
      searchPagination(page, pagen) {
        var vm = this; 
        ...
      },
      _rawsearchonurl() {
        let ampersand = "&page=";
        this.searchPagination(1, ampersand);
      }
    },
    created() {
      this.searchonurl = debounce(this._rawsearchonurl, 500);
      ...
    }