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.

5
  • in my view , this line <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"> Commented Dec 17, 2014 at 8:47
  • I tried : data-to= "{{total}}" data-to= {{total}} data-to= "total" data-to= total .. no succes ! Commented Dec 17, 2014 at 9:03
  • In the browser consol i have this error : "countUp error: startVal or endVal is not a number" Commented Dec 17, 2014 at 9:05
  • 1
    startVal and endVal are variables of Joignable.js in my code are data-from="1" data-to= "{{total}}" Commented Dec 17, 2014 at 9:21
  • check out the support site (search xe-counter) link Commented Oct 22, 2015 at 19:57

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

5 Comments

Can you please show me how should my original directive should look like to distinguish the 'to' variable ?
I don't know where you are exactly planning to use the total variable but I updated the jsfiddle . 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
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
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
There are different possibilities, one is in this jsfiddle. 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.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.