Angular Directive Does Not Evaluate Inside ng-repeat
Solution 1
Agree that you need to think about where the directive begins and ends. Here's a plnkr that illustrates a directive bound to each item in the array - http://plnkr.co/edit/IU8fANreYP7NYrs4swTW?p=preview
If you want the directive to encapsulate the enumerating of a collection defined by a parent scope it gets a bit tricker. I'm not sure if the following is 'best practice', but it's how i've handled it -- http://plnkr.co/edit/IU8fANreYP7NYrs4swTW?p=preview
When relying on the directive to perform the iteration you get involved with transclusion, which is a made up word that means (as i understand it) take the content defined in the parent, push it into the directive and then evaluate it. I've been working with angular for a few months, and I'm starting to think that asking the directive to iterate is a smell, and I've always been able to design around it.
Solution 2
Ignoring all the theoretical aspects, you can get your code to work by making two simple changes.
- don't use mixed case in your attribute names.
displaytext
notdisplayText
- put the
<td>
tags outside the directive, in the template
Do that and it will work; it think those are both Angular bugs.
Solution 3
i think the right way to approach this would be to send the object into admin roster item, like this:
<tr ng-repeat="position in positions">
<admin-roster-item pos="position">
</admin-roster-item>
</tr>
and in the directive:
var app = angular.module("MyApp", []);
app.directive("adminRosterItem", function () {
return {
restrict: "E",
scope: {
pos: "@"
},
template: "<td>{{ formattedText }}</td>", // should I have this?
link: function(scope, element, attrs){
// all of this can easily be done with a filter, but i understand you just want to
// know how it works
scope.formattedText = scope.pos.Name + ' (' + scope.pos.Code + ')';
}
}
});
PS. i didn't test this!
Solution 4
Instead of writing your directive as a child of ng-repeat, try keeping the custom directive on the same level as ng-repeat, this
<tr ng-repeat="position in positions" admin-roster-item displayText="{{ position.Name + ' (' + position.Code + ')' }}"></tr>
And furthermore, allow your custom directive to be used as an attribute. AngulaJS has defined ng-repeats priority as 1000, so at times when you custom directive is made, it does not go down well with ng-repeat.
A second option (try only if the first one fails) is to set the priority of your custom directive more than that of ngRepeat i.e. to 1001.
Mickael Caruso
Enthusiast Programmer working in mostly C#, the .net Framework, SQL Server, Flex/Actionscript, and (new to) Android.
Updated on July 13, 2022Comments
-
Mickael Caruso almost 2 years
I have the following setup:
App/Directive
var app = angular.module("MyApp", []); app.directive("adminRosterItem", function () { return { restrict: "E", scope: { displayText: "@" }, template: "<td>{{ displayText }}</td>", // should I have this? link: function(scope, element, attrs){ // What do I put here? I don't seem to have any // element to initialize (set up event handlers, for example) }, compile: function(?,?,?){} // should I have this? If so, what goes inside? } });
Controller
function PositionsController($scope) { $scope.positions = [{ Name: "Quarterback", Code: "QB" }, { Name: "Wide Receiver", Code: "WR" } ]; }
HTML:
<div ng-app="MyApp"> <div ng-controller="PositionsController"> <table> <tr ng-repeat="position in positions"> <admin-roster-item displayText="{{ position.Name + ' (' + position.Code + ')' }}"></admin-roster-item> </tr> </table> </div> </div>
It's a very simple example, but I can't get it to render. Perhaps there's something that tutorials aren't telling me, or that is secret Angular knowledge?
If I remove the directive inside the
<tr ng-repeat="..." />
and place<td>{{ displayText }}</td>
instead, it will show all records.But I want the directive to be more complicated than just a single
<td>{{}}</td>
(eventually) so that I could reuse this directive in multiple apps.So, I'm really asking how do we properly create a directive that goes inside ng-repeat? What am I missing? What should be taken off from the code above?
-
Mickael Caruso about 11 yearsNot sure what you mean by "think about where the directive begins and ends." I did look at the plnkr code and got the pattern. The use of the isolate "=" seems to work for this one. I also removed the table, tr, and td and used divs instead. It didn't work with the table structure but it did with the divs.
-
DusanV about 11 yearsI mean that it's important to think about the directive in terms of what value is it providing to the the view that is using it. I find that directives are easiest to manage if they are small, tightly encapsulated chunks of ui + behavior. If I lose site of the core functionality that the directive is meant to provide (and start it starts taking on too many responsibilities) the complexity starts to overwhelm.
-
Mickael Caruso about 11 yearsShocking! It works even when I didn't apply the other advices above (though, they're still good to know). The table handling seems to be "uncooperative", though - tried making the template root a tr and put ng-repeat on the table - didn't work. Switched everything to div and span, and it works, though layout looks messed up.
-
jlb about 11 years"Transclusion" is not a made up word: en.wikipedia.org/wiki/Transclusion :)
-
fiatjaf almost 11 yearsSugestion 2 solves it. Sugestion 1 is unnecessary (use camelCase on javascript, hyphened-tags on HTML).