1

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)

1 Answer 1

1

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}".

Sign up to request clarification or add additional context in comments.

2 Comments

I like the angular's inline templates, any option with that? also what's the purpose of toLocaleLowerCase()
can you please do a without DRY approach, I wanna see multiple filters applied inline, one for the search field and the other for attendee.chat option

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.