2

I'm trying to build a timer using Vue.js and I want it to output the time left.

new Vue({
    el: "#app",
    data: {
        timer: 25,
        message2: "",
        counter: false,
        interval: null
    },
    methods: {
        startTimer() {
            this.interval = setInterval(this.countDown(), 1000);
        },
        countDown() {
            if (this.counter === false) {
                var n = this.timer;
                this.counter = true;
            } else if (n > 0) {
                n -= 1;
                this.message2 = "You have " + n + "seconds left.";
            } else {
                clearInterval(this.interval);
                this.counter = false;
                this.message2 = "Your time is up!"
            }
        },

But I cannot get the message2 updated every time Interval runs the function countDown(). Actually it doesn't run the function every second but only once.

Yanek Yuk
  • 74
  • 3
  • 11
  • 1
    You're executing `undefined` in setInterval because you're *calling* your countDown method which doesn't return a function. – Wabbitseason Jun 09 '17 at 09:34
  • I'm not sure that I understand. I've always thought of methods as functions. – Yanek Yuk Jun 09 '17 at 09:37
  • `this.interval = setInterval(this.countDown, 1000);` remove the () from the function. `setInterval` is expecting a function as 1st parameter. That means you pass it a function reference like `this.countDown` or declare an anonymous function like `function() { //dostuff}`. Check this out for further information about function calls/references: https://stackoverflow.com/questions/15886272/what-is-the-difference-between-a-function-call-and-function-reference – xetra11 Jun 09 '17 at 09:42

2 Answers2

4

You should pass a function to setInterval, but since you execute this.countDown immediatly, you are passing the result of that call to setInterval:

startTimer() {
  this.interval = setInterval(this.countDown(), 1000);
}

to pass the function instead of calling it, leave out the ()

startTimer() {
  this.interval = setInterval(this.countDown, 1000);
}

also, I fixed some mistakes in countdown() (e.g. defining n in the if branch, but using it in the else if, so it's undefined there, or only changing the local n, which would not change this.timer ...)

countDown() {
  var n = this.timer
  if (!this.counter) {
    this.counter = true;
  } else if (n > 0) {
    n = n - 1
    this.timer = n
    this.message2 = "You have " + n + "seconds left."
  } else {
    clearInterval(this.interval);
    this.counter = false
    this.message2 = "Your time is up!"
  }
},
Linus Borg
  • 23,622
  • 7
  • 63
  • 50
  • thanks but after the timer ends, ```this.timer``` should stay as it was before. taking the ```var n = this.timer``` outside of if complicates more than it simplifies in this scenario. Creating a data object for counter seems like a better alternative maybe. – Yanek Yuk Jun 09 '17 at 11:15
1

As others have pointed out, you need to pass your function to setInterval, not invoke it and pass its return value.

Furthermore, when you execute the statement n = this.timer, you are invoking the getter that Vue set up on this.timer and copying the very unmagical number 25 to your variable n. n then goes out of scope when the function returns and your copy of 25 is lost. The next time your function runs (another second later) n is undefined. This results in your else clause clearing the interval. Your elseif clause never runs.

I think the smallest change to make your code work would be to promote n to live in your data: add n: undefined, to your data:{}, so that it can be tracked by Vue, then replace all your uses of n with this.n.

Once you've got that working, I'd recommend simplifying some things:

  • setting this.n = this.timer should probably be done in startTimer
  • if you set this.interval = undefined when you clear your interval, this.counter becomes redundant, because you can just check if this.interval is truthy
  • you can turn message2 into a "computed"
  • you can add an if clause to startTimer to prevent a second interval from being added if there's already one active (e.g. if someone clicks a "start timer" button twice before the timer runs down)
chriswa
  • 128
  • 2
  • 7