Vue equivalent of setTimeout?

Javascriptvue.js

Javascript Problem Overview


I'm making a shopping cart system with Laravel and Vue. When I add an item to the basket, I display a confirmation message by toggling a Vue variable being watched by a v-if:

<div class="alert alert-success" v-if="basketAddSuccess" transition="expand">Added to the basket</div>

And the JS:

addToBasket: function(){
				item = this.product;
				this.$http.post('/api/buy/addToBasket', item);
				this.basketAddSuccess = true;
			}

(And yes, I will be adding this in a then-catch shortly).

This works fine and the message appears. However, I'd like the message to disappear again after a certain time, say a few seconds. How can I do this with Vue? I've tried setTimeOut but Vue doesn't seem to like it, saying it's undefined.

EDIT: I was misspelling setTimeout like an idiot. However, it still doesn't work:

My function is now:

addToBasket: function(){
				item = this.photo;
				this.$http.post('/api/buy/addToBasket', item);
				this.basketAddSuccess = true;
                setTimeout(function(){
                    this.basketAddSuccess = false;
                }, 2000);
			}

Javascript Solutions


Solution 1 - Javascript

Arrow Function

The best and simplest way to solve this problem is by using an arrow function () => {}:

    addToBasket() {
        var item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        // now 'this' is referencing the Vue object and not the 'setTimeout' scope
        setTimeout(() => this.basketAddSuccess = false, 2000);
    }

This works because the this of arrow functions is bound to the this of its enclosing scope- in Vue, that's the parent/ enclosing component. Inside a traditional function called by setTimeout, however, this refers to the window object (which is why you ran into errors when you tried to access this.basketAddSuccess in that context).

Argument Passing

Another way of doing this would be passing this as an arg to your function through setTimeout's prototype using its setTimeout(callback, delay, arg1, arg2, ...) form:

    addToBasket() {
        item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        //Add scope argument to func, pass this after delay in setTimeout
        setTimeout(function(scope) {
             scope.basketAddSuccess = false;
        }, 2000, this);
    }

(It's worth noting that the arg passing syntax is incompatible with IE 9 and below, however.)

Local Variable

Another possible, but less eloquent and less encouraged, way is to bind this to a var outside of setTimeout:

    addToBasket() {
        item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        //Declare self, which is accessible inside setTimeout func
        var self = this;
        setTimeout(function() {
             self.basketAddSuccess = false;
        }, 2000);
    }

Using an arrow function would eliminate the need for this extra variable entirely however, and really should be used unless something else is preventing its use.

Solution 2 - Javascript

after encountering the same issue, I ended up on this thread. For future generation's sake: The current up-most voted answer, attempts to bind "this" to a variable in order to avoid changing the context when invoking the function which is defined in the setTimeout.

An alternate and more recommended approach(using Vue.JS 2.2 & ES6) is to use an arrow function in order to bind the context to the parent (Basically "addToBasket"'s "this" and the "setTimeout"'s "this" would still refer to the same object):

addToBasket: function(){
        item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        setTimeout(() => {
            this.basketAddSuccess = false;
        }, 2000);
    }

Solution 3 - Javascript

Add bind(this) to your setTimeout callback function

setTimeout(function () {
    this.basketAddSuccess = false
}.bind(this), 2000)

Solution 4 - Javascript

ES6 can bind 'this'

setTimeout(() => {

 },5000);

Solution 5 - Javascript

vuejs 2

first add this to methods

methods:{
    sayHi: function () {
      var v = this;
      setTimeout(function () {
		v.message = "Hi Vue!";
    }, 3000);
   }

after that call this method on mounted

mounted () {
  this.sayHi()
}

Solution 6 - Javascript

Kevin Muchwat above has the BEST answer, despite only 10 upvotes and not selected answer.

setTimeout(function () {
    this.basketAddSuccess = false
}.bind(this), 2000)

Let me explain WHY.

"Arrow Function" is ECMA6/ECMA2015. It's perfectly valid in compiled code or controlled client situations (cordova phone apps, Node.js), and it's nice and succinct. It will even probably pass your testing!

However, Microsoft in their infinite wisdom has decided that Internet Explorer WILL NOT EVER SUPPORT ECMA2015!

Their new Edge browser does, but that's not good enough for public facing websites.

Doing a standard function(){} and adding .bind(this) however is the ECMA5.1 (which IS fully supported) syntax for the exact same functionality.

This is also important in ajax/post .then/else calls. At the end of your .then(function){}) you need to bind(this) there as well so: .then(function(){this.flag = true}.bind(this))

I would have added this as a comment to Kevin's response, but for some silly reason it takes less points to post replies than to comment on replies

DO NOT MAKE THE SAME MISTAKE I MADE!

I code on a Mac, and used the 48 points upvoted comment which worked great! Until I got some calls on my scripts failing and I couldn't figure out why. I had to go back and update dozens of calls from arrow syntax to function(){}.bind(this) syntax.

Thankfully I found this thread again and got the right answer. Kevin, I am forever grateful.

As per the "Accepted answer", that has other potential issues dealing with additional libraries (had problems properly accessing/updating Vue properties/functions)

Solution 7 - Javascript

You can use Vue.nextTick

addToBasket: function(){
                item = this.photo;
                this.$http.post('/api/buy/addToBasket', item);
                this.basketAddSuccess = true;
                Vue.nextTick(() =>{
                    this.basketAddSuccess = false;
                });
            }

Solution 8 - Javascript

Arrow Function

The best and simplest way to solve this problem is by using an arrow function () => {}:

    addToBasket() {
        var item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        // now 'this' is referencing the Vue object and not the 'setTimeout' scope
        setTimeout(() => this.basketAddSuccess = false, 2000);
    }

This works because the this of arrow functions is bound to the this of its enclosing scope- in Vue, that's the parent/ enclosing component. Inside a traditional function called by setTimeout, however, this refers to the window object (which is why you ran into errors when you tried to access this.basketAddSuccess in that context).

Argument Passing

Another way of doing this would be passing this as an arg to your function through setTimeout's prototype using its setTimeout(callback, delay, arg1, arg2, ...) form:

    addToBasket() {
        item = this.photo;
        this.$http.post('/api/buy/addToBasket', item);
        this.basketAddSuccess = true;
        //Add scope argument to func, pass this after delay in setTimeout
        setTimeout(function(scope) {
             scope.basketAddSuccess = false;
        }, 2000, this);
    }

(It's worth noting that the arg passing syntax is incompatible with IE 9 and below, however.)

Solution 9 - Javascript

If you want to use the this keyword in your function, you need to write the setTimeout function in ES6

setTimeout(() => {
   this.filters.max_budget_gt_eq = this.budgetHigherValue;
}, 1000);

Solution 10 - Javascript

There's no need for bind(this) when you are using arrow functions:

  setTimeout( ()=> {
    // some code
   }, 500)

Solution 11 - Javascript

use this.animationStop, not use this.animationStop ( )

animationRun(){
    this.sliderClass.anim = true;
    setTimeout(this.animationStop, 500);
},

Solution 12 - Javascript

It is likely a scope issue. Try the following instead:

addToBasket: function(){
    item = this.photo;
    this.$http.post('/api/buy/addToBasket', item);
    this.basketAddSuccess = true;
    var self = this;
    setTimeout(function(){
        self.basketAddSuccess = false;
    }, 2000);
}

Solution 13 - Javascript

please use setInterval like below:

    setInterval(()=>{this.checkOrder()},2000);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionflurpleplurpleView Question on Stackoverflow
Solution 1 - Javascriptg.annunziataView Answer on Stackoverflow
Solution 2 - JavascriptSamuel BergströmView Answer on Stackoverflow
Solution 3 - JavascriptKevin MuchwatView Answer on Stackoverflow
Solution 4 - Javascript陈奕璇View Answer on Stackoverflow
Solution 5 - JavascriptmohamadView Answer on Stackoverflow
Solution 6 - JavascriptPeterView Answer on Stackoverflow
Solution 7 - JavascriptSamundra KhatriView Answer on Stackoverflow
Solution 8 - Javascriptzcoop98View Answer on Stackoverflow
Solution 9 - JavascriptWouter SchoofsView Answer on Stackoverflow
Solution 10 - JavascriptArminView Answer on Stackoverflow
Solution 11 - JavascriptPax ExterminatusView Answer on Stackoverflow
Solution 12 - JavascriptPedroView Answer on Stackoverflow
Solution 13 - Javascriptpirooz jenabiView Answer on Stackoverflow