Reputation: 2538
I'm trying to create a directive that accepts an array of objects and run ng-repeat to render them.
app.directive("rockers", function(){
return {
restrict : "E",
replace : true,
scope : {
items : '='
},
template : '<div>'+
'<span ng-repeat="item in items">{{item.name}} Rocks!</span>'+
'</div>'
};
});
In the controller, I set the scope with opjects:
app.controller("appController", function($scope){
$scope.scopedItems = [{name:"Aanny"}, {name:"Bonny"}, {name:"Danny"}];
});
And then, to call the directive I pass the scopedItems with filter, like this:
<div ng-app="myApp" ng-controller="appController">
Rockers:
<rockers items="scopedItems | filter:{name:'Bonny'}"></rockers>
</div>
Without using the filters in the HTML, everything works fine. When passing the filter I still get the results I want, but I'm also getting this error: "Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!"
What am I doing wrong?
Here is a jsFiddle link to recreate the error.
Thanks!
Upvotes: 3
Views: 5241
Reputation: 563
I found a very simple solution involving adding another two-way bound scope variable to the directive and filtering inside the directive template instead of inside the html:
<div ng-app="myApp" ng-controller="appController">
<input type="text" ng-model="find"><br>
Rockers:
<rockers items="scopedItems" filter="find"></rockers>
</div>
Angular gives digest errors when a filtered array expression is passed in as a two-way bound directive scope variable. By using the filter attribute, you can filter anything you type in the input field, or use a $scope object to defined your filter e.g. $scope.find = {name: 'Bonny'};
You could even use a scope filter function or pre-defined filter, anything you would pass into a real filter expression will work because of the two-way binding.
var app = angular.module('myApp', []);
app.controller("appController", function($scope){
$scope.scopedItems = [{name:"Aanny"}, {name:"Bonny"}, {name:"Danny"}];
});
app.directive("rockers", function($filter){
return {
restrict : "E",
replace : true,
scope : {
items : '=',
filter: '='
},
template : '<div>'+
'<span ng-repeat="item in items | filter:filter">{{item.name}} Rocks!</span>'+
'</div>'
};
});
Working example: http://jsfiddle.net/2jhswna6/
Upvotes: 0
Reputation: 416
You could also use memoization in a custom filter to return the calculated value unless the input value changes.
See this example: Memoizing Angular filters to stop digest errors
Upvotes: 0
Reputation: 2267
Also you can pass filtered data to directive like a string:
<rockers items="{{ scopedItems | filter:{name:'Bonny'} }}"></rockers>
and parse it value to object it directive:
app.directive("rockers", function(){
return {
restrict : "E",
replace : true,
scope : {},
link:function(scope, elem, attr){
scope.items = JSON.parse(attr.items);
},
template : '<div>'+
'<span ng-repeat="item in items">{{item.name}} Rocks!</span>'+
'</div>'
};
});
Upvotes: 3
Reputation: 2267
You can pass rocker name into directive by attribute and filter it there:
<div ng-app="myApp" ng-controller="appController">
Rockers:
<rockers name="Bonny" items="scopedItems"></rockers>
</div>
And in directive:
app.directive("rockers", function(){
return {
restrict : "E",
replace : true,
scope : {
items : '='
},
link:function(scope, elem, attr){
scope.filteredItems = function(filterItemName){
return scope.items.filter(function(item){
return item.name == filterItemName;
})
};
scope.filteredItem = scope.filteredItems(attr.name);
},
template : '<div>'+
'<span ng-repeat="item in filteredItem">{{item.name}} Rocks!</span>'+
'</div>'
};
});
Upvotes: 1