7

I need to display the spinner for at least 2 seconds if ajax has been completed before transitioning.

I have the following but isnt working

route: {
    data(transition) {

        this.getDelayedData();
        transition.next();
       }
    },

methods:{   
   getSliders(){
            this.$http.get('/api/sliders/getsliders')
                   .then(sliders =>{this.sliders = sliders.data

              });
        },
   getPosts(){
            this.$http.get('/api/posts/getposts')
                    .then(posts =>{this.posts = posts.data.data

                    });
        },
   getDelayedData(){
       function timer() {
           var dfd = $.Deferred();
           setTimeout(function()
                    {
                    console.log("done");
                    }, 2000,dfd.resolve);
                    return dfd.promise();
                    }
           $.when( this.getPosts(), this.getSliders(),timer() )
                .done();
       }
    }

I tried to implement the code reading this post But the $.when function isnt waiting until the setTimeout function is finished executing.

Community
  • 1
  • 1
FerchoCarcho
  • 531
  • 8
  • 20
  • I am seeing few more things here... Your `function timer() {...}` is creating a new scope inside. So, technically your `this.getPosts()` and `this.getSliders()` should not even work inside the `timer` function. You may need to start using [arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) everywhere as a habit. – Mani Oct 26 '16 at 02:38

2 Answers2

9

You have already accepted my other answer, but I think the code can be improved significantly, as explained below:

It is generally recommended not to mix jQuery with Vue.js, as both modify DOM:

  • Vue.js has a very strong hold on the DOM, for its model-view bindings, using its Reactivity System

  • jQuery may modify using class or id selectors, if you use it for some quick-fix solution somewhere.

You will end up spending a lot of time debugging frontend issues, which takes focus away from the business requirements. Also, Vue.js already requires modern browsers, so technically jQuery is not doing much for browser compatibility - its primary goal.

So if you are open to leaving jQuery behind, here is another method:

getDelayedData() {
    // Create a new Promise and resolve after 2 seconds
    var myTimerPromise = new Promise((resolve, reject) => {
        setTimeout(() => {
            // callback function for timer, gets called after the time-delay
            // Your timer is done now. Print a line for debugging and resolve myTimerPromise
            console.log("2 seconds up, resolving myTimerPromise")
            resolve();
        }, 2000);  // This promise will be resolved in 2000 milli-seconds
    });

    // Start displaying ajax spinner before fetching data
    this.displaySpinner = true;

    // Fetch data now, within Promise.all()
    Promise.all([myTimerPromise, this.getPosts(), this.getSliders()]).then( () => {
        // Hide the spinner now. AJAX requests are complete, and it has taken at least 2 seconds.
        this.displaySpinner = false;
        console.log("Data fetched for sliders and posts");
    });
}

The above code also requires you to have this.displaySpinner, which activates or deactivates your ajax spinner. Or you may have some other way of doing it.

And you still need to return this.$http.get(..) from your getPosts and getSliders methods.

Note: I haven't tested the above code, you need to debug if required. It should work, based on my understanding of Promise.all() as given in this reference doc: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Once it starts working, you can simplify myTimerPromise as follows:

getDelayedData() {
    // Create a new Promise and resolve after 2 seconds
    var myTimerPromise = new Promise((resolve, reject) => {
        setTimeout(resolve, 2000);  // "resolve" is already a function, no need for another anonymous function here
    });
    // and do the other stuff here...
}

I would prefer this method, as it avoids jQuery. Then my Vue.js app can be as small as 30 KB gzipped, whereas if I bundle jQuery along, it can go well over 100 KB. It is just a personal choice :-)

Mani
  • 23,635
  • 6
  • 67
  • 54
  • Amazing Answer @Mani, you have taught me so many things. Of course I will leave jquery, just let me tell you for the Spinner Im using the $loadingRouteData var when the Data Route Hook is reached. I just need to Study your code and make the changes. then I will tell you how was it all. Once Again thanks!! – FerchoCarcho Oct 26 '16 at 11:54
  • @FerchoCarcho Thanks for your feedback, and also thanks for making this the chosen answer. I guess this will help the community better than my other answer. – Mani Oct 26 '16 at 11:57
  • Sorry for the delayed answer. Timezones :D. you are true again, I have hard time to set things in one place, but your guide is very useful. – FerchoCarcho Oct 26 '16 at 12:00
2

I believe the problem is caused because your methods - getSliders() and getPosts() are not returning any Promises. So your deferred timer has no way of knowing what is going on in these get methods.

Your this.$http.get(..) from Vue-resource returns a promise. You can return it as follows:

getSliders(){
    // Fetch sliders and "return" the promise object
    return this.$http.get('/api/sliders/getsliders')
        .then(sliders =>{
            // The data on vue is already being set here. So it will not wait for timeout.
            this.sliders = sliders.data
            // Instead you may try to set it on "this.sliders_temp" and then change later to "this.sliders"
        });
}, // and so on...

That follows the example given here: http://www.erichynds.com/blog/using-deferreds-in-jquery, which is linked in the other stackoverflow answer that you referred to in the question. They return $.get(..) in their functions.

But as indicated in my code sample above, even if you return this.$http.get(...), it will still not work for you, because you are already setting this.sliders inside your success handler for $http. So your Vue template will update the UI immediately. You need to think through a better strategy for your app.

Mani
  • 23,635
  • 6
  • 67
  • 54
  • If you don't mind the data getting updated on page, and just want the spinner for 2 seconds, then simply `return this.$http.get(..)` will work, no other changes required. – Mani Oct 26 '16 at 01:27
  • Thank you @Mani for the help :D – FerchoCarcho Oct 26 '16 at 02:02
  • I believe it can be significantly improved. Check out my other answer, if it helps :-) – Mani Oct 26 '16 at 02:29