AngularJS directive with ng repetition, non-hanging

The problem is that I need to manage a list of chewing balls that are retrieved from the service. The directive I created works when I hard-code HTML elements, but when I try to dynamically distribute chewing gum balls using ng-repeat.

HTML

<div ng-controller="GumballsCtrl"> <h1>Working</h1> <ul> <li ng-repeat="gumball in Gumballs"> <div class="gumballColor{{gumball.color}}">{{gumball.color}}</div> </li> </ul> <h1>Problem - Expecting the same result at the work version</h1> <ul> <li ng-repeat="gumball in Gumballs"> <mygumball id={{gumball.id}} color="{{gumball.color}}">{{gumball.color}}</mygumball> </li> </ul> </div> 

Javascript

 var myApp = angular.module('myApp', []); function GumballsCtrl($scope, Gumballs) { $scope.Gumballs = Gumballs; } myApp.factory('Gumballs', function () { return [{ id: '1', color: 'R' }, { id: '2', color: 'G' }, { id: '3', color: 'B' }, { id: '4', color: 'Y' }, { id: '5', color: 'G' }]; }); myApp.directive('mygumball', function ($scope) { return { restrict: 'E', scope: {}, link: function (scope, element, attrs) { if (attrs.color !== '' && attrs.color !== undefined) { scope.color = attrs.color; } else { scope.color = 'U'; } }, replace: true, template: "<div class='gumballColor{{color}}'>{{color}}</div>" }; }); 

CSS

 .gumballColorR { font-size: 12px; text-align: center; padding: 2px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: solid 1px #CC0000; background-color: #FF0000; width: 15px; height: 15px; margin-left: 5px; margin-top: 5px; } .gumballColorG { font-size: 12px; text-align: center; padding: 2px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: solid 1px #00CC00; background-color: #00FF00; width: 15px; height: 15px; margin-left: 5px; margin-top: 5px; } .gumballColorB { font-size: 12px; text-align: center; padding: 2px; color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: solid 1px #0000CC; background-color: #0000FF; width: 15px; height: 15px; margin-left: 5px; margin-top: 5px; } .gumballColorY { font-size: 12px; text-align: center; padding: 2px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: solid 1px #CCCC00; background-color: #FFFF00; width: 15px; height: 15px; margin-left: 5px; margin-top: 5px; } .gumballColorU { font-size: 12px; text-align: center; padding: 2px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: solid 1px #CCCCCC; background-color: #DDDDDD; width: 15px; height: 15px; margin-left: 5px; margin-top: 5px; } 

http://jsfiddle.net/i3sik/NGB9v/22/

The id and color identifiers when transmitted in the directive end undefined when transferred using ng-repeat, but work when they are hard-coded in HTML.

Thanks so much for any help you can provide.

+7
source share
1 answer

The problem here is in your area of โ€‹โ€‹isolation. Using scope: {} , you created a new one, isolate the scope for this element. Isolated areas are not inherited from the parent area. All attributes and contents of directives with allocation areas are evaluated in the context of the isolation area. gumball does not exist in the selection area, so everything looks like undefined.

You have two options to fix this: (1) remove the selection area (for example, scope: true to create a child area); or (2) bind values โ€‹โ€‹in the selection area.

To bind your attributes to scope variables, you just need to specify the scope and type of binding that you want:

 scope: { id: '@', color: '@' }, 

This suggests that the id and color attributes should be interpolated in the context of the parent area and then added to the area. You can remove all this logic inside your link function - this will do it for you.

But this still leaves a content problem inside the directive. To interpolate this in the context of the parent area, you need to do the transition:

 transclude: true, template: "<div class='gumballColor{{color}}' ng-transclude></div>" 

Transclusion takes the contents of an element and interpolates it relative to the new child of the parent area, for example. where gumball will still be determined.

With these two changes, your directive will work as desired.

If you are confused about which area to use, here's another question that might help: When writing a directive, how can I decide if I need a new area, a new content area, or a new selection area?


Side note: Even without a highlighting area, the logic in your link function to determine attribute values โ€‹โ€‹will not work. The order of execution is the important part, which roughly corresponds to: compiler โ†’ controller โ†’ link โ†’ interpolation. Until interpolation is complete, there is no value for your attributes. This way your checks will not work.

However, you can set $observe to interpolated attributes; $observe will always be run for the first time, even if no value has been passed. You can use this to set your default value. $observe also very efficient.

 attrs.$observe( 'attr1', function(val) { if ( !angular.isDefined( val ) ) { scope.attr1 = 'defaultValue'; } }); 
+10
source

All Articles