1

I have the following setup

HTML:

<span star score="{{resto.rating}}"></span>

A controller that downloads data and sets it in resto and the directive below. My problem is that the link function is being called before the score expression has been interpolated, so I only ever get 1 star.

angular.module 'afmnewApp'
.directive 'star', () ->
    return {
        scope: 
            score: '@'
        template : """
            <ul class="rating">
                <li ng-repeat="star in stars" ng-class="star">
                    *
                </li>
            </ul>
        """,
        link : (scope, elem, attrs) ->
            scope.stars = []
            for x in [1..scope.score]       # should it be attrs?
                scope.stars.push({'fill'})
    }
Simon H
  • 19,279
  • 13
  • 64
  • 116

1 Answers1

1

I had a similar issue on Friday :-). I managed to find my own solution. In your case it would be :

angular.module 'afmnewApp'
.directive 'star', () ->
    return {
        template : """
            <ul class="rating">
                <li ng-repeat="star in stars" ng-class="star">
                    *
                </li>
            </ul>
        """,
        scope:{ 
            score: '@score'
        },
        link: function (scope, element, attrs) {
            scope.$watch('score', function (newValue, oldValue) {
                if (newValue !== undefined) {
                    scope.stars = []
                    for x in [1..scope.score]
                        scope.stars.push({'fill'})
                }
            });
        }
    };
});

So in the link you add a watch on your variable, so when it changes the watch will fire populating your array.

Edit based on Jesus comment below:

I checked about the difference between $watch and $observe. Here is a nice SO post describing it. It seems that for checking attribute changes in DOM objects (as in your case the score attribute of your span object) you should use $observe. For anything else you should use $watch. So in this case the solution should better be:

angular.module 'afmnewApp'
.directive 'star', () ->
    return {
        template : """
            <ul class="rating">
                <li ng-repeat="star in stars" ng-class="star">
                    *
                </li>
            </ul>
        """,
        scope:{ 
            score: '@score'
        },
        link: function (scope, element, attrs) {
            attrs.$observe('score', function (newValue, oldValue) {
                if (newValue !== undefined) {
                    scope.stars = []
                    for x in [1..scope.score]
                        scope.stars.push({'fill'})
                }
            });
        }
    };
});
Community
  • 1
  • 1
Giannis Paraskevopoulos
  • 17,836
  • 1
  • 48
  • 66
  • Even when your answer is technically correct, I suggest using an $observer instead of $watch here. – Jesus Rodriguez Sep 08 '14 at 08:52
  • I have to admit that my Angular dev experience is little. I will check out the $observer and update accordingly. – Giannis Paraskevopoulos Sep 08 '14 at 08:56
  • @JesusRodriguez Thanks for your comment. I did some research and have updated my answer. – Giannis Paraskevopoulos Sep 08 '14 at 11:57
  • I marked this as the accepted answer because it does the job, but I can't help thinking there must be a better way, and that this must be a common enough situation to warrant the angular team ensuring better ordering of compilation? So I have unmarked 'accepted' for the time being – Simon H Sep 09 '14 at 13:44