Reputation: 7330
I've an ng-repeat list with multiple filters.
<div class="search pull-right">
<div class="magnify"><img src="img/magnify.png" ng-click="magnify()"></div>
</div>
<div class="search-box" ng-show="showSearch">
<input type="search" id="searchField" name="" value="" placeholder="Search" class="textfield" ng-model="search.firstName" />
</div>
List 1 (attendee.chat is not 0) gets affect by search TextField
<ul class="table-view">
<li class="table-view-cell attendee" ng-repeat="attendee in attendees | filter:search | filter:attendee.chat">
<h2 class="name">{{attendee.firstName}} {{attendee.lastName}}</h2>
<span class="job">{{attendee.title}}</span>
<span class="company">{{attendee.company}}</span>
</li>
</ul>
List 2 (attendee.chat is 0) but also gets affect by search TextField
<ul class="table-view">
<li class="table-view-cell attendee" ng-repeat="attendee in attendees | filter:search | filter:attendee.chat">
<h2 class="name">{{attendee.firstName}} {{attendee.lastName}}</h2>
<span class="job">{{attendee.title}}</span>
<span class="company">{{attendee.company}}</span>
</li>
</ul>
any DRY approach would also be appreciated since I've two similar templates up there. (difference is attendee.chat it can be 0 or any positive number)
Upvotes: 1
Views: 1126
Reputation: 38103
To make the markup more DRY, you could create a directive:
app.directive('attendeeDetails', function () {
return {
restrict: 'E',
replace: true, // even though this option is deprecated
scope: {
attendee: '&' // don't need 2-way binding
}
template: '<li class="table-view-cell attendee">' +
'<h2 class="name">{{attendee().firstName}} {{attendee().lastName}}</h2>' +
'<span class="job">{{attendee().title}}</span>' +
'<span class="company">{{attendee().company}}</span>' +
'</li>'
};
});
Then just change your markup to use it:
List 1:
<ul class="table-view">
<attendee-details attendee="attendee" ng-repeat="attendee in attendees | filter:list1Filter"></attendee-details>
</ul>
List 2:
<ul class="table-view">
<attendee-details attendee="attendee" ng-repeat="attendee in attendees | filter:list2Filter"></attendee-details>
</ul>
Then write the two simple filter functions in the controller:
$scope.list1Filter = function (attendee) {
return matchesSearch(attendee) && attendee.chat > 0;
};
$scope.list2Filter = function (attendee) {
return matchesSearch(attendee) && attendee.chat === 0;
};
function matchesSearch(attendee) {
return attendee.firstName.toLocaleLowerCase().indexOf($scope.search.firstName.toLocaleLowerCase()) >= 0;
}
(I am using .toLocaleLowerCase()
instead of .toLowerCase()
because that has become my preference so it will cater for any locale specific cases. Saves me effort if I ever had to port my code to work in Turkey, for instance.)
You could also use templates together with ng-include
like so:
<script type="text/ng-template" id="/attendee-details.html">
<h2 class="name">{{attendee.firstName}} {{attendee.lastName}}</h2>
<span class="job">{{attendee.title}}</span>
<span class="company">{{attendee.company}}</span>
</script>
Then your markup would become as follows:
List 1:
<ul class="table-view">
<li class="table-view-cell attendee"
ng-include="'/attendee-details.html'"
ng-repeat="attendee in attendees | filter:list1Filter">
</li>
</ul>
List 2:
<ul class="table-view">
<li class="table-view-cell attendee"
ng-include="'/attendee-details.html'"
ng-repeat="attendee in attendees | filter:list2Filter">
</li>
</ul>
Note the distinct disadvantage with this approach is if you want to create valid HTML markup is that you cannot put the <li>
inside the template, since the contents of the template will be inserted inside the node with ng-include
on it. Since valid HTML can't have anything inside a <ul>
other than <li>
s, you need to duplicate the <li>
and its classes for both lists.
The reason I chose to go with the controller function for the filter, rather than using multiple filters inline in the expression is that your criteria for list 1 of showing all elements with attendee.chat > 0
is not possible to express using any of the builtin filters as far as I can see. You cannot specify an expression for the thing to match against.
You could use multiple filters for List 2, though, since you are only checking for a single value for attendee.chat
(0).
You could do ng-repeat="attendee in attendees | filter:{firstName:search.firstName,chat:0}"
, or if you had to have multiple filters instead (which will be slower, so I don't know why you would), you could do ng-repeat="attendee in attendees | filter:search | filter:{chat:0}"
.
Upvotes: 1