Jesse Carter
Jesse Carter

Reputation: 21147

Dynamically Updating Directive (1.2 rc3)

I've been playing around with directives for the first time today and am trying to build a reusable progress bar directive (based on Bootstrap 3.0) that I can dynamically have fill up or empty based on a value. The directive definition is as follows:

directive('progressBar', function () {
    var success = 'progress-bar progress-bar-success';
    var warning = 'progress-bar progress-bar-warning';
    var danger = 'progress-bar progress-bar-danger';

    var setCssStyling = function (width) {
        if (width >= 50) {
            return success;
        } else if (width >= 20) {
            return warning;
        } else {
            return danger;
        }
    }

    var formatWidth = function (width) {
        return 'width: ' + width + '%';
    }

    return {
        restrict: 'E',
        scope: {},
        template: '<div class="progress progress-striped active">' +
            '<div ng-class="cssStyle" role="progressbar" style="{{ width }}"></div>' +
            '</div>',
        link: function (scope, element, attrs) {
            if (attrs.width) {
                scope.width = formatWidth(attrs.width);
            } else {
                scope.width = formatWidth(0);
            }
            scope.$watch(attrs.width, function (newVal) {
                scope.width = formatWidth(newVal);
                scope.cssStyle = setCssStyling(newVal);
            });
        }
    }
});

This works exactly as planned given these test usages:

<progress-bar width="100"></progress-bar>
<progress-bar width="45"></progress-bar>
<progress-bar width="15"></progress-bar>

What I was hoping to do was be able to dynamically bind the width attribute to a changing value in my controller such that the width and styling of the progress bar would move. I tried binding to a value in my controller that changed every second:

<progress-bar width="{{ today.seconds }}"></progress-bar>

But when I examine the scope for that progress-bar the width is always set to width: undefined%. Is there a better way to accomplish dynamically updating content like this or am I missing something when it comes to scope or something silly?

Upvotes: 0

Views: 78

Answers (2)

ops.rio
ops.rio

Reputation: 552

In your directive you need to use

scope: { width: '=' }

= this kind of parameter says to angular to create a bi-directional parameter in your directive scope, so, if you change it's value in your directive your controller will be affected and if you change this value in your controller your directive will be reflected

Upvotes: 1

Zach Snow
Zach Snow

Reputation: 1044

You are using scope.$watch on attrs.width, which means it shouldn't be an interpolation. So you should simply do <progress-bar width="today.seconds"></progress-bar>.

If you'd prefer to be able to use an interpolation, you'd use scope.$observe, and you'd also want to parse the width (as the interpolation will return a string).

Update: since you've set up an isolate scope (scope: {}) you will need to bind width in your scope hash object.

return {
    restrict: 'E',
    scope: { width: '='},
    template: '<div class="progress progress-striped active">' +
        '<div ng-class="cssStyle" role="progressbar" style="{{ cssWidth }}"></div>' +
        '</div>',
    link: function (scope, element, attrs) {
        scope.cssWidth = '';
        scope.$watch('width', function (newVal) {
            scope.cssWidth = formatWidth(newVal);
            scope.cssStyle = setCssStyling(newVal);
        });
    }
}

Of course you can also just ditch the isolate scope if that makes sense in your context. Also you could drop cssWidth all together and simply do your formatting in the style attribute.

http://plnkr.co/edit/Zx1fgHYyXuxByHH6YorP

Upvotes: 1

Related Questions