3

A simple show and hide on the loading element is failing during the Vue js function execution.

I have a vue js function which will do some things and update the data when it's done. I'm trying to show a loading animation during this process. I've dummied the process down to a simple loop for the purposes of this discussion to eliminate all other issues (i.e ajax, async, etc.).

My HTMl for the button looks like this:

<button type="button" v-on:click="DoStuff()">Do Stuff</button>

My vue js code looks like this:

var client = new Vue({
    el: '#client',
    data: {
        someData: []
},
methods: {
    DoStuff: function() {

        //Show Loader
        $(".loader").show();

        //Waste 5 seconds
        for (var i = 0; i < 100000000; i++) {
            var x = i;
        }

        //Hide loader
        $(".loader").hide();

        // let me know it's done.
        alert('cool');
    }

The loader never shows. If I comment out the hide command, the loader show up AFTER the alert. Makes me think some sort of async operation is going on under the hood but I'm not doing async stuff.

1
  • 1
    Something to help explain the current problem: the DOM won't be rendered until your function is finished. You can read more here: stackoverflow.com/questions/30188845/… Commented Feb 6, 2019 at 22:25

4 Answers 4

8

You don't need to use jQuery to set and hide elements on the page based on a condition, in fact that kind of defeats the purpose of what VueJS and other front end javascript frameworks are used for.

First you should add a property called loading to your data object

data: {
   someData: [],
   loading: false
}

Then, in your doStuff function, remove the jquery lines and set the loading property accordingly

methods: {
    doStuff: function() {
        //Show Loader
        this.loading = true;

        //Waste 5 seconds
        setTimeout(() => {
           this.loading = false;
        }, 5000)
    }
}

Finally, in your view, add this line

<div v-if="loading">Loading some data</div>

Ill end this by saying that I also think your snippet is a little messed up. the methods property should be inside the Vue instance definition.

var client = new Vue({
    el: '#client',
    data: {
        someData: [],
        loading: false
    },
    methods: {
       doStuff: function() {
          //Show Loader
          this.loading = true;

          //Waste 5 seconds
          setTimeout(() => {
            this.loading = false;
          }, 5000)
       }

   }
}

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. See my response to perellorodrigo below. This approach is not scalable across many forms and pages.
@ArdAtak You still don't, and shouldnt be using jquery. This is the right approach, you just need loading defined in the parent components and pass it down to child props. Also just saying I think you have a wrong understanding of the word scalable
2

Don't use jquery. You can so this with vuejs.


var client = new Vue({
    el: '#client',
    data: {
        someData: [],
        loading: false,
},
methods: {
    DoStuff() {

        //Show Loader
        this.loading = true;

        //Waste 5 seconds

        //Hide loader
        this.loading = true;

        // let me know it's done.
        alert('cool');
    }

And your HTML.

<div class="loading" v-if="loading">
    Loading....
</div>
<div v-else>
The rest of your website 
</div>

Comments

0

It's better to not mix with JQuery, just use Vue conditional displaying instead (v-show).

In your loader:

<div class="loader" v-show="loading"></div>

In your Vue code:

var client = new Vue({
    el: '#client',
    data: {
        someData: [],
        loading: false
},
methods: {
    DoStuff: function() {

        this.loading = true;

        //Waste 5 seconds
        for (var i = 0; i < 100000000; i++) {
            var x = i;
        }

        this.loading = false;

        // let me know it's done.
        alert('cool');
    }

2 Comments

Thanks. The problem is I'm trying to use ONE global loading animation on the parent page and manipulate it from the global commands that make ajax calls. So I don't want to pepper every form and every function with separate loading animations and loading elements. I tried to simplify the problem to show that any time a vue js button is clicked and the function is executed the most basic Show / Hide is failing. Although, as I mentioned, if I comment out the Hide command the loader does show up AFTER the alert.
Okk, if that's the case you should use an Event Bus. By using events you’re not limited to child-parent relationships. So whenever you make any request, you fire the event in any child and catch it in the parent. Have a read on this article, you will be probably more interested in the final part of it:flaviocopes.com/vue-components-communication
-3

Thanks Everyone. I have to focus on a more important feature that came up. But I just wanted to share what I've gleamed. This is not a (or the) final answer but it explains the problem and the correct approach.

The problem was correctly identified by Andrew Lohr as is further explained here: jQuery hide() and show() not immediately run when reversed later in the function

The best (but not only) solution would be use an event bus as mentioned by perellorodrigo here: https://flaviocopes.com/vue-components-communication/

I will post my final solution when I get around to it.

1 Comment

the correct approach is not using jquery with vue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.