Approach 1
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.)
Approach 2
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.
Why Not Multiple Filters?
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}".