Build A Pomodoro Timer with Vue.js (Solution to Code Challenge #6)

William Imoh
👁️ 3,057 views
💬 comments

Tried the code challenge #6? Last week, we put out the challenge to build a Pomodoro timer using any tool or technology.

You can check out the amazing entries for the challenge using the hashtag #scotchchallenge on twitter on in the comment section under the challenge's post. In this post, we shall be solving the challenge using Vue.js

Why Vue.js?

Vue is a progressive JavaScript framework for developing interactive user interfaces. Vue offers the ease to manipulate DOM elements as well as apply methods to these elements, seamlessly.

Table of Contents

    The Base

    The base code provided consists of HTML and CSS code to structure and style the timer in its default state.

    HTML

    The HTML structure of the timer is divided into several parts for clarity and better control. The parts are the body, the timer numbers, and the timer control buttons.

    <!-- our template -->
    <section id="app" class="hero is-info is-fullheight is-bold">
    <div class="hero-body">
    <div class="container has-text-centered">
    
    <!-- The bulk of the timer -->
    
    </div>
    </div>
    </section>

    First, we create the display text on the timer and the timer numbers with:

    <h2 class="title is-6">Let the countdown begin!!</h2>
    
        <!--  THE TIMER NUMBERS  -->
      <div id="timer">
        <span id="minutes">25</span>
        <span id="middle">:</span>
        <span id="seconds">00</span>
      </div>

    Next, we include the buttons for control with:

    <!--  THE BUTTONS  -->
    <div id="buttons">
    
    <!--  START BUTTON    -->
    <button 
      id="start" 
      class="button is-dark is-large"> 
        <i class="far fa-play-circle"></i>
    </button>
    
    <!--   PAUSE BUTTON   -->
    <button 
      id="stop" 
      class="button is-dark is-large"> 
        <i class="far fa-pause-circle"></i>
    </button>
    
    <!--  RESET BUTTON   -->
    <button 
      id="reset" 
      class="button is-dark is-large"> 
        <i class="fas fa-undo"></i>
    </button>
    </div>

    CSS

    Basic CSS was used to style the timer. Bulma and Fontawesome were imported as external sheets to the pen. The timer was styled with:

    #message {
      color: #DDD;
      font-size: 50px;
      margin-bottom: 20px;
    }
    
    #timer {
      font-size: 200px;
      line-height: 1;
      margin-bottom: 40px;
    }

    The Technique

    We are required to countdown the time from 25minutes 00seconds. To implement this dynamism, Vue was required to manipulate the timer values as well as implement the controls of pause, play, and restart on the timer.

    Creating The Vue Application

    First, we create a new Vue instance and mount it on the DOM element with the id of app, using the Vue property, el. In the Vue instance, we also define the required lifecycle methods and properties.

    const app = new Vue({
      el: '#app',
      // ========================
      data: {
    
      },
      // ========================
      methods: {
    
      },
      // ========================
      computed: {
    
      }
    })

    The data property holds the state data as well as dynamic data we would like to pass into the DOM. The methods property contains all methods required by the timer, this property will hold the controls of the timer. Many times we would like to perform further manipulation on the items in the data property before using them elsewhere in an application, the computed property holds all manipulated data.

    The Data

    In the data object, we created all the basic data required by the timer as well as the timer state.

    timer: null,
    totalTime: (25 * 60),
    resetButton: false,
    title: "Let the countdown begin!!"

    totalTime which is the total time to be run by the timer in seconds will be further split into minutes and seconds. The timer property holds the state of the timer and resetButton holds the state of the reset button with which we can toggle the button. The title property is used to dynamically set the display text above the timer.

    The Methods

    We configure the timer controls in the methods object with:

    startTimer: function() {
      this.timer = setInterval(() => this.countdown(), 1000);
      this.resetButton = true;
    },
    stopTimer: function() {
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = true;
    },
    resetTimer: function() {
      this.totalTime = (25 * 60);
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = false;
    },
    padTime: function(time) {
      return (time < 10 ? '0' : '') + time;
    },
    countdown: function() {
      this.totalTime--;
    }

    Each timer method makes use of the state values in data to create actions on the timer. A setInterval() function starts the timer and calls the countdown method which in turn decrements the total time every 1000milliseconds (1 second). The stopTimer methods employs a clearInterval() function to stop the timer currently running.

    The resetTimer method simply returns the total time on the timer to its initial value, stops the timer from running and removes the reset button (obviously isn't needed anymore). To make the timer look better, we introduce a padTime function which includes a leading zero (0) to either the second or the minute value once it is less than 10.

    In the computed property, we calculate the value of minutes and seconds from the totalTime. This value of minutes and seconds will be passed to the DOM.

    Passing Timer Variables and Methods to the DOM

    So far we have created all the required variables, state values, and methods for controls. Vue provides a clear way to pass this data into the DOM using handlebars-like syntax. We pass in the minutes and seconds variable with:

    <div id="timer">
        <span id="minutes">{{ minutes }}</span>
        <span id="middle">:</span>
        <span id="seconds">{{ seconds }}</span>
    </div>

    We currently have three buttons in place, but not all should display at once. We would display the reset button and either of the pause and play button at any time. To achieve this, we conditionally render the buttons with respect to the state of the timer, using the v-if Vue directive.

    <div id="buttons">
    <!--     Start TImer -->
        <button 
          id="start" 
          class="button is-dark is-large" 
          v-if="!timer"
          @click="startTimer">
            <i class="far fa-play-circle"></i>
        </button>
        <!--     Pause Timer -->
        <button 
          id="stop" 
          class="button is-dark is-large" 
          v-if="timer"
          @click="stopTimer">
            <i class="far fa-pause-circle"></i>
        </button>
        <!--     Restart Timer -->
        <button 
          id="reset" 
          class="button is-dark is-large" 
          v-if="resetButton"
          @click="resetTimer">
            <i class="fas fa-undo"></i>
        </button>
    </div>

    The conditional v-if directive renders the button element if its assigned value resolves to either true or false depending on which is chosen. This value is set dynamically with Vue. The @click directive (short for v-on:click) triggers the assigned method when clicked.

    Bonus

    To make the timer more fun, and of course, inspiring, we dynamically set the text above the timer to change with each state of the timer. We already set this text using Vue in the data property. We then proceed to change the value of this text once a method is triggered. Just to stress more on setting DOM values with Vue, in the methods property we have:

    startTimer: function() {
        this.timer = setInterval(() => this.countdown(), 1000);
        this.resetButton = true;
        this.title = "Greatness is within sight!!"
    },
    stopTimer: function() {
        clearInterval(this.timer);
        this.timer = null;
        this.resetButton = true;
        this.title = "Never quit, keep going!!"
    },
    resetTimer: function() {
        this.totalTime = (25 * 60);
        clearInterval(this.timer);
        this.timer = null;
        this.resetButton = false;
        this.title = "Let the countdown begin!!"
    },

    Notice how this.title changes with each method.

    You can see the final product here https://codepen.io/Chuloo/pen/xWVpqq

    Conclusion

    Pomodoro timers are great for productivity, in this challenge we built out one using Vue. We also demonstrated several features that makes Vue interesting, including Vue's lifecycle methods and properties. Feel free to leave your comments under this post and let's look forward to the next challenge. Happy coding!

    William Imoh

    24 posts

    Developer, Developer Advocate, Changing the world one semi-colon at a time!