20

I am trying to allow a user to reset or shutdown a given server from an app. Im working on the interface right now, and want to give the user messages as to what is happening. I display a message defined in my data object to indicate the action taken. I thene use setTimeout to switch a resetting.... message with a reset message. See the following method.

    systemReset: function(){
            this.message = this.server + ': Resetting';
            setTimeout(function(){
                this.message = this.server + ': Reset';
            }, 2000);

    } 

In my browser I can trigger this message and my message of "Resetting" displays, but the following "Reset" message is never output. Do I have any formatting errors?

To put this method in context here is my entire component.

  <template>
    <div>
      <p>{{message}}</p>
      <button @click="systemReset">Reset Server</button>
      <button @click="systemPowerDown">Poweroff Server</button>
    </div>
  </template>

  <script type="text/javascript">
    export default{
      data: function(){
        return{
          message: ''
        }
      },
      methods: {
        systemPowerDown: function(){
            this.message = this.server + ': Server Down';
        },
        systemReset: function(){
            this.message = this.server + ': Resetting';
            setTimeout(function(){
                this.message = this.server + ': Reset';
            }, 2000);
         }
      },
      props: ['server']
    }
  </script>

Am I missing something obvious?  Or is there some vue limitation I am unaware of?  
DMrFrost
  • 903
  • 2
  • 13
  • 33

5 Answers5

46

The value of this is different inside the setTimeout.

If you're using ES6, you can use an arrow function:

setTimeout(() => { this.message = this.server + ': Reset' }, 2000)

Or if you're not, you can bind the value of this:

setTimeout(function () {
  this.message = this.server + ': Reset'
}.bind(this))

However, having never used Vue, I don't know if it will know to re-render when you change the value of this.message, or if you should be changing some component state or something.

Tommy Brunn
  • 2,520
  • 2
  • 29
  • 41
6

Because you're inside a setTimeout, this doesn't correspond with your Vue instance. You can use self instead :

systemReset: function(){
    this.message = this.server + ': Resetting';
    var self = this;
    setTimeout(function(){
        self.message = self.server + ': Reset';
    }, 2000);
}
Treast
  • 1,095
  • 1
  • 9
  • 20
2

Could be solved of storing this in a variable out of the timeout function?

Like so:

 systemReset: function(){
            var $this = this;
            $this.message = this.server + ': Resetting';
            setTimeout(function(){
                $this.message = this.server + ': Reset';
            }, 2000);
         }

Doing so refers to the correct function systemReset

Patrick McDermott
  • 1,220
  • 1
  • 15
  • 30
0

I had a familiar problem. So the solution was to create a function (in 'methods') that changed the variable. Then from the 'setInterval' I called this method (this.methodname).

D. Dimopoulos
  • 315
  • 2
  • 6
0

Use $forceUpdate() if nothing helped.

The appropriate passage of this to setTimeout() works fine in JSFiddle https://jsfiddle.net/BeloglazovRL/owL94phz/ (Vue 2.6.14). But it doesn't work with my web application which use Vue 2.6.13.

Call to Vue this.$forceUpdate(); helped me with a periodically called setTimeout().

I have tired all other answers: saving this to self, arrow function and explicit bind. The debug output prints changing variable and correct this with Vue inner stuff, but nothing was changing on the screen. It changed only after the timer ends. I tried set timeout to 5 seconds instead 1 second. It didn't help too. Then I decided to force update. E.g.: Can you force Vue.js to reload/re-render?

It helped me do solve the problem. So you have to use the code like this:

myTimer() {

  ... //Change text, e.g.: %, timer and so on. Check condition for halt.

  this.vueTextVar = newTextValue;
  this.$forceUpdate();
  setTimeout(() => {
          this.myTimer();
        }, 1000);
}