Let's manage time!
At this point of time, I already know that you are so, so, so enthusiastic with this book that you want to read it to the end without stopping. But this is not right. We should manage our time and give us some time to work and some time to rest. Let's create a small application that implements a Pomodoro technique timer so that it can help us in our working time management.
Note
The Pomodoro technique is a time management technique named after the kitchen tomato timer (in fact, Pomodoro means tomato in Italian). This technique consists of breaking down the working time into small intervals separated by short breaks. Read more about the Pomodoro technique on the official site: http://pomodorotechnique.com/ .
Thus, our goal is very simple. We just have to create a very simple time counter that will decrement untill the end of the working interval and then restart and decrement till the end of the resting time and so on.
Let's do that!
We will introduce two Vue data variables, minute
and second
, which will be displayed on our page. The main method on each second will decrement second
; it will decrement minute
when second
becomes 0
; and when both minute
and second
variables come to 0
, the application should toggle between working and resting interval:
Our JavaScript code will look like the following:
const POMODORO_STATES = { WORK: 'work', REST: 'rest' }; const WORKING_TIME_LENGTH_IN_MINUTES = 25; const RESTING_TIME_LENGTH_IN_MINUTES = 5; new Vue({ el: '#app', data: { minute: WORKING_TIME_LENGTH_IN_MINUTES, second: 0, pomodoroState: POMODORO_STATES.WORK, timestamp: 0 }, methods: { start: function () { this._tick(); this.interval = setInterval(this._tick, 1000); }, _tick: function () { //if second is not 0, just decrement second if (this.second !== 0) { this.second--; return; } //if second is 0 and minute is not 0, //decrement minute and set second to 59 if (this.minute !== 0) { this.minute--; this.second = 59; return; } //if second is 0 and minute is 0, //toggle working/resting intervals this.pomodoroState = this.pomodoroState === POMODORO_STATES.WORK ? POMODORO_STATES.REST : POMODORO_STATES.WORK; if (this.pomodoroState === POMODORO_STATES.WORK) { this.minute = WORKING_TIME_LENGTH_IN_MINUTES; } else { this.minute = RESTING_TIME_LENGTH_IN_MINUTES; } } } });
In our HTML code, let's create two placeholders for minute
and second
, and a start button for our Pomodoro timer:
<div id="app" class="container"> <h2> <span>Pomodoro</span> <button @click="start()"> <i class="glyphicon glyphicon-play"></i> </button> </h2> <div class="well"> <div class="pomodoro-timer"> <span>{{ minute }}</span>:<span>{{ second }}</span> </div> </div> </div>
Again, we are using Bootstrap for the styling, so our Pomodoro timer looks like the following:
Countdown timer built with Vue.js
Our Pomodoro is nice, but it has some problems:
- First of all, we don't know which state is being toggled. We don't know if we should work or rest. Let's introduce a title that will change each time the Pomodoro state is changed.
- Another problem is inconsistent display of minutes and seconds numbers. For example, for 24 minutes and 5 seconds, we would like to see 24:05 and not 24:5. Let's fix it introducing computed values in our application data and displaying them instead of normal values.
- Yet another problem is that our start button can be clicked over and over again, which creates a timer each time it's clicked. Try to click it several times and see how crazy your timer goes. Let's fix it by introducing start, pause, and stop buttons, apply application states to them, and disable buttons to the state accordingly.
Toggle the title by using computed properties
Let's start by fixing the first problem by creating computed property title and using it in our markup.
Note
Computed properties are the properties inside the data
object that allow us to avoid blowing up the template with some extra logic. You can find more information about computed properties on the official documentation site:
http://vuejs.org/guide/computed.html
.
Add the computed
section in the Vue options
object and add the property title
there:
data: { //... }, computed: { title: function () { return this.pomodoroState === POMODORO_STATES.WORK ? 'Work!' : 'Rest!' } }, methods: { //...
And now just use the following property as it was a normal Vue data
property in your markup:
<h2>
<span>Pomodoro</span>
<!--!>
</h2>
<h3>{{ title }}</h3>
<div class="well">
And voilà! Now we have a title that changes each time the Pomodoro state is being toggled:
Automatic change of the title based on the state of the timer
Nice, isn't it?
Left-pad time values using computed properties
Now let's apply the same logic for left padding our minute
and second
numbers. Let's add two computed properties, min
and sec
, in our computed
section in the data
options and apply the simple algorithm to pad the numbers with 0
on the left. Of course, we could use a famous left-pad project (
https://github.com/stevemao/left-pad
), but to keep things simple and not to break the whole Internet (
http://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/
), let's apply a simple logic of our own:
computed: { title: function () { return this.pomodoroState === POMODORO_STATES.WORK ? 'Work!' : 'Rest!' }, min: function () { if (this.minute < 10) { return '0' + this.minute; } return this.minute; }, sec: function () { if (this.second < 10) { return '0' + this.second; } return this.second; } }
And let's use these properties instead of minute
and second
in our HTML code:
<div class="pomodoro-timer">
<span>{{ min }}</span>:<span>{{ sec }}</span>
</div>
Refresh a page and check how beautiful our numbers are now:
Left padding using computed properties in Vue.js
Keep state with start, pause, and stop buttons
So, to fix the third problem, let's introduce three application states, started
, paused
, and stopped
, and let's have three methods that would allow us to permute over these states. We already have the method that starts the application, so we just add the logic there to change the state to started
. We also add two additional methods, pause
and stop
, which would pause the timer and change to the corresponding application state:
const POMODORO_STATES = { WORK: 'work', REST: 'rest' }; const STATES = { STARTED: 'started', STOPPED: 'stopped', PAUSED: 'paused' }; //<...> new Vue({ el: '#app', data: { state: STATES.STOPPED, //<...> }, //<...> methods: { start: function () { this.state = STATES.STARTED; this._tick(); this.interval = setInterval(this._tick, 1000); }, pause: function () { this.state = STATES.PAUSED; clearInterval(this.interval); }, stop: function () { this.state = STATES.STOPPED; clearInterval(this.interval); this.pomodoroState = POMODORO_STATES.WORK; this.minute = WORKING_TIME_LENGTH_IN_MINUTES; this.second = 0; }, //<...> } });
And, let's add two buttons to our HTML code and add the click
listeners that call the corresponding methods:
<button :disabled="state==='started'" @click="start()"> <i class="glyphicon glyphicon-play"></i> </button> <button :disabled="state!=='started'" @click="pause()"> <i class="glyphicon glyphicon-pause"></i> </button> <button :disabled="state!=='started' && state !== 'paused'" @click="stop()"> <i class="glyphicon glyphicon-stop"></i> </button>
Now our application looks nice and allows us to start, pause, and stop the timer:
Toggling start, stop, and pause buttons according to the application state
Check what the whole code looks like in JSFiddle at https://jsfiddle.net/chudaol/b6vmtzq1/1/.
After so much work and so many of new terms and knowledge, you certainly deserve a kitten! I also love kittens, so here you have a random kitten from the awesome site http://thecatapi.com/ :