JS Timer using AlpineJs with Carbon Format

If you have used Carbon Class in PHP, then you might have heard about the method Carbon::diffForHumans(). It returns the difference between 2 dates in a Human Readable Form.

So if difference is less than 60 seconds, the output would be in seconds. If the difference is more than 60 seconds, the output would be in minutes. If the difference is more than 60 minutes, the output would be in hours and so on.

Recently I had to built a Timer in JS which should the elapsed Time in a similar manner. I choose to built the Timer using AlpineJS.

I choose to call my Component moment because I have been a big fan of moment.js. The Component had a prop of seconds which would hold the number of seconds that timer would need to display.

<span x-data="moment">
</span>

<script>
    function moment() {
        return {
            seconds: 1,
        }
    }
</script>

Next I created an init method which would set the initial value of the timer.

<span x-data="moment" x-init="init(40)">
</span>

<script>
    function moment() {
        return {
            seconds: 1,
            init(seconds) {
                this.seconds = seconds;
            },
        }
    }
</script>

Next, within this init method, I would use setInterval to call a closure after each second. Within the closure, I would increment the value of seconds prop. I would also create an interval prop which I could use to close the Timer.

interval: "",
init(seconds) {
    this.seconds = seconds;
    this.interval = setInterval(() => {
        this.seconds++;
    }, 1000);
},

Next I will create a method to display the Timer, I will call it getTimeElapsed and use it as below:

<span x-data="moment" x-init="init(40)">
    <span x-text="getTimeElapsed"></span>
</span>

.
.
.
            getTimeElapsed() {
                return this.seconds;
            }

At this stage the Timer would be working well and it will increment after each second. Now, we would format the getTimeElapsed method so that it would return the data similar to Carbon method.

In order to do so, I created an intervals property like below:

    intervals: [
      { label: "hour", seconds: 3599 },
      { label: "minute", seconds: 59 },
      { label: "second", seconds: 1 }
    ],

And then I used this property within the getTimeElapsed as follows:

getTimeElapsed() {
    const interval = this.intervals.find((i) => i.seconds < this.seconds);
    const count = Math.floor(this.seconds / interval.seconds);
    return `${count} ${interval.label}${count !== 1 ? "s" : ""} ago`;
}

This will display the difference in seconds and as soon as the difference crosses 59 seconds, it would display the difference in minutes and so on. I only needed difference till hours so I only defined interval props till 3600. If you need to display days, you can define further.

My last requirement was to Stop the Timer as soon as it crossed 2 hours. So I used the following check in getTimeElapsed.

if (this.seconds > 7200) {
    clearInterval(this.interval);
}

The beauty of AlpineJS is that you can define multiple of these Components on your page and each will behave independently of each other. You can check the implementation at the CodePen.

Hope you have enjoyed this tutorial. 

Leave a Reply

Your email address will not be published. Required fields are marked *