6

i have a controller named "HomeCtrl" which calculates the total number of user's into the {{total}} binding variable, like this:

.controller('HomeCtrl', function($scope, $http){
    $scope.total = 0;
});

In my view, I am trying to display my total in an animated widget by passing {{total}} as the value of an attribute on a <div> tag, like this:

<div ng-controller="HomeCtrl" ng-init="init('users')">
    <div class="xe-widget xe-counter xe-counter-green" xe-counter 
       data-count=".num" data-from="1" 
       data-to= "{{total}}" 
       data-suffix="users" data-duration="3" data-easing="true">
          <div class="xe-icon">
                  <i class="linecons-user"></i>
          </div>
          <div class="xe-label">
                  <strong class="num">1k</strong>
                  <span>Users Total </span>
          </div>
   </div>            
   <center> <b> Total utilisateurs : {{total}} </b> </center>     

Here is the widget directive:

.directive('xeCounter', function(){     
        return {
            restrict: 'EAC',
            link: function(scope, el, attrs)
            {
                var $el = angular.element(el),
                    sm = scrollMonitor.create(el);
                sm.fullyEnterViewport(function()
                {
                    var opts = {
                        useEasing:      attrDefault($el, 'easing', true),
                        useGrouping:    attrDefault($el, 'grouping', true),
                        separator:      attrDefault($el, 'separator', ','),
                        decimal:        attrDefault($el, 'decimal', '.'),
                        prefix:         attrDefault($el, 'prefix', ''),
                        suffix:         attrDefault($el, 'suffix', ''),
                    },
                    $count      = attrDefault($el, 'count', 'this') == 'this' ? $el : $el.find($el.data('count')),
                    from        = attrDefault($el, 'from', 0),
                    to          = attrDefault($el, 'to', ''),
                    duration    = attrDefault($el, 'duration', 2.5),
                    delay       = attrDefault($el, 'delay', 0),
                    decimals    = new String(to).match(/\.([0-9]+)/) ? new String(to).match(/\.([0-9]+)$/)[1].length : 0,
                    counter     = new countUp($count.get(0), from, to, decimals, duration, opts);                   
                    setTimeout(function(){ counter.start(); }, delay * 1000);

                    sm.destroy();
                });
            }
        };
    })

I can display the correct value of {{total}} in my view, but when I pass {{total}} into the attribute, as data-to= "{{total}}", it does not work. It doesn't recognize it as a number.

Reid Spencer
  • 2,776
  • 28
  • 37
Alaa-GI
  • 410
  • 1
  • 5
  • 19

3 Answers3

4

This works for me: The Number is retrieved using a async webservice call when the controller loads. That means the value isn't there when the sm.fullyEnteredViewport function is called. But luckily you can set a data-delay attribute on the xe-counter element.

<div class="xe-widget xe-counter" xe-counter data-delay="1" data-count=".num" data-from="0" data-to="{{ total }}" data-suffix="" data-duration="5">
<div class="xe-icon"><i class="linecons-cup" /></div>
<div class="xe-label"><strong class="num">0</strong><span>TOTAL</span></div>
</div>

Then modify your directive like this: Retrieve the delay value from element and put the rest of the other code into the delayed function like this:

directive('xeCounter', function () {
    return {
        restrict: 'EAC',
        link: function (scope, el, attrs) {

            var $el = angular.element(el),
                sm = scrollMonitor.create(el);
          
                //get the delay attribute from element
                var delay = attrs.delay ? attrs.delay : 0;

            sm.fullyEnterViewport(function () {

                setTimeout(function () {
                    // get all values from element delayed and start the counter
                    var opts = {
                        useEasing: attrDefault($el, 'easing', true),
                        useGrouping: attrDefault($el, 'grouping', true),
                        separator: attrDefault($el, 'separator', ','),
                        decimal: attrDefault($el, 'decimal', '.'),
                        prefix: attrDefault($el, 'prefix', ''),
                        suffix: attrDefault($el, 'suffix', '')
                    },
                    $count = attrDefault($el, 'count', 'this') == 'this' ? $el : $el.find($el.data('count')),
                    from = attrs.from ? attrs.from : 0,
                    to = attrs.to ? attrs.to : 100,
                    duration = attrs.duration ? attrs.duration : 2.5,
                    decimals = String(to).match(/\.([0-9]+)/) ? String(to).match(/\.([0-9]+)$/)[1].length : 0,
                    counter = new countUp($count.get(0), from, to, decimals, duration, opts);
                    counter.start();
                }, delay * 1000);

                sm.destroy();
            });
        }
    };
}).
Your webservice should return within the delay time. That does the trick for me.
1

You have to distinguish, because if you are just using {{total}} in your directive template it works, because angular automatically will look for total in the parent controller if it is not found in the local one.

On the other hand, if you want to use the value of total in an isolated scope, you can bind to your data-to attribute, and this is achieved by following code:

app.directive('xeCounter', function(){
 return {
    restrict: 'EA',
    scope: { to: '@' },
    link: function(scope)
    {
        console.log(scope.to);
    }
 };
});

Btw. I changed the restrict from EAC to EA, because otherwise your directive is going to be applied twice and breaks. And you have to put the asterisks around the attribute value.

See the full working example here.

Manuel B.
  • 115
  • 7
  • Can you please show me how should my original directive should look like to distinguish the 'to' variable ? – Alaa-GI Dec 17 '14 at 11:11
  • 1
    I don't know where you are exactly planning to use the total variable but I updated the [jsfiddle](http://jsfiddle.net/tcydmhms/1/) . There are still some things missing because I don't know where your objects like scrollMonitor come from... Look at the part `to = scope.to` – Manuel B. Dec 17 '14 at 14:35
  • Thank you, btw it shows me the initial value of total which is 0 instead of the updated total, do you have any idea to how take the last value of my {{total}} ? thank's a lot – Alaa-GI Dec 17 '14 at 14:53
  • console.log(scope.to); ===> 0 but in my controller i do something like that $scope.total= $scope.compteur; $scope.compteur is the number o users – Alaa-GI Dec 17 '14 at 14:56
  • 1
    There are different possibilities, one is in this [jsfiddle](http://jsfiddle.net/tcydmhms/3/). The basic things I did here, is to change the attribute from `data-to="{{total}}"` to `data-to=total`, in the directive replaced @ with =, and added a watch function in the `link` of the directive. – Manuel B. Dec 17 '14 at 15:44
1

This works for me.

Replace yours directive for this:

directive('xeCounter', function () {
    return {
        restrict: 'EAC',
        link: function (scope, el, attrs) {

            var $el = angular.element(el),
                sm = scrollMonitor.create(el);

            sm.fullyEnterViewport(function () {
                var opts = {
                        useEasing: attrDefault($el, 'easing', true),
                        useGrouping: attrDefault($el, 'grouping', true),
                        separator: attrDefault($el, 'separator', ','),
                        decimal: attrDefault($el, 'decimal', '.'),
                        prefix: attrDefault($el, 'prefix', ''),
                        suffix: attrDefault($el, 'suffix', '')
                    },
                    $count = attrDefault($el, 'count', 'this') == 'this' ? $el : $el.find($el.data('count')),
                    from = attrs.from ? attrs.from : 0,
                    to = attrs.to ? attrs.to : 100,
                    duration = attrs.duration ? attrs.duration : 2.5,
                    delay = attrs.delay ? attrs.delay : 0,
                    decimals = String(to).match(/\.([0-9]+)/) ? String(to).match(/\.([0-9]+)$/)[1].length : 0,
                    counter = new countUp($count.get(0), from, to, decimals, duration, opts);

                setTimeout(function () {
                    counter.start();
                }, delay * 1000);

                sm.destroy();
            });
        }
    };
}).

And the HTML should stay like this:

<div class="xe-widget xe-counter" xe-counter data-count=".num" data-from="0" data-to="{{ total }}" data-suffix="" data-duration="5">
<div class="xe-icon"><i class="linecons-cup" /></div>
<div class="xe-label"><strong class="num">0</strong><span>TOTAL</span></div>
</div>

Be shure that the $scope.total variable is declared and its value is an number.

leoalmar
  • 226
  • 5
  • 14