A countUp.js AngularJS directive

The rendered countUp.js directive in action
Here’s an AngularJS directive for the countUp.js library. As the library authors put it, “countUp.js is a dependency-free, lightweight JavaScript class that can be used to quickly create animations that display numerical data in a more interesting way.”

In other words, it gives numbers a nice animated look. This especially cool for livening up dashboards, or other information-rich pages.

The only downside to the library was its actual usage. It looked something like this:

The old way, in CoffeeScript:

1
new countUp("uniqueOpens", parseInt($("#uniqueOpens").text()), $scope.uniqueOpens, 0, 2).start()

The old HTML markup:

1
<h1 id="uniqueOpens">0</h1>

Writing new countUp... ten or more times in my Angular controller got old, fast.

Like any good programmer, I did a bunch of Googling to figure out the solution. After a solid 5 minutes, I was suprised to see that there wasn’t an Angular directive for this floating around. So, I ended up taking things into my own hands.

The solution, in all its CoffeeScript glory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.directive "countUp", ->
restrict: "A" # For use on attributes only
require: "ngModel" # Must have an ngModel to get the countUp value from
scope: true # Will not create an isolate, child scope. If we put an object here, it will
link: (scope, element, attrs) ->
numDecimals = 0 # Default to show 0 decimals
animationLength = 4 # Default to animate for 4 secs
if attrs.numDecimals? and attrs.numDecimals >= 0
numDecimals = attrs.numDecimals
if attrs.animationLength? and attrs.animationLength > 0
animationLength = attrs.animationLength
scope.$watch attrs.ngModel, (newVal, oldVal) ->
if !oldVal? then oldVal = 0
if newVal? and newVal isnt oldVal
new countUp(attrs.id, oldVal, newVal, numDecimals, animationLength).start()

And its vanilla JS counterpart (if you’re into that sort of thing):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
module.directive("countUp", function() {
return {
restrict: "A",
require: "ngModel",
scope: true,
link: function(scope, element, attrs) {
var animationLength, numDecimals;
numDecimals = 0;
animationLength = 4;
if ((attrs.numDecimals != null) && attrs.numDecimals >= 0) {
numDecimals = attrs.numDecimals;
}
if ((attrs.animationLength != null) && attrs.animationLength > 0) {
animationLength = attrs.animationLength;
}
return scope.$watch(attrs.ngModel, function(newVal, oldVal) {
if (oldVal == null) {
oldVal = 0;
}
if ((newVal != null) && newVal !== oldVal) {
return new countUp(attrs.id, oldVal, newVal, numDecimals, animationLength).start();
}
});
}
};
});

The usage is as follows:

1
<h1 count-up id="opens" ng-model="dynamicStats.opens"></h1>

Note that you need the attribute count-up, ng-model, as well as an id. The count-up attribute is self explanatory, while the ng-model is the numerical value to apply the countUp effect to. The id is necessary for countUp itself- it doesn’t matter what the name of the id is.

Optionally, you can configure the number of decimals and the animation length (in seconds) like so:

1
<h1 count-up id="opens" ng-model="dynamicStats.opens" data-num-decimals="2" data-animation-length="10"></h1>

This directive is a work in progress- there are probably a ton of optimzations that can be made. Let me know if you have suggestions, and hopefully this helps out somebody!