AngularJS : Custom filters and ng-repeat
Asked Answered
O

4

71

I'm an AngularJS newbie and I'm building up a small proof-of-concept car hire listings app that pulls in some JSON and renders out various bits of that data via an ng-repeat, with a couple of filters:

   <article data-ng-repeat="result in results | filter:search" class="result">
        <header><h3>{{result.carType.name}}, {{result.carDetails.doors}} door, &pound;{{result.price.value}} - {{ result.company.name }}</h3></header>
            <ul class="result-features">
                <li>{{result.carDetails.hireDuration}} day hire</li>
                <li data-ng-show="result.carDetails.airCon">Air conditioning</li>
                <li data-ng-show="result.carDetails.unlimitedMileage">Unlimited Mileage</li>
                <li data-ng-show="result.carDetails.theftProtection">Theft Protection</li>
            </ul>
    </article>

    <h2>Filters</h2>

    <h4>Doors:</h4> 
    <select data-ng-model="search.carDetails">
        <option value="">All</option>
        <option value="2">2</option>
        <option value="4">4</option>
        <option value="9">9</option>
    </select>

    <h4>Provider:</h4>
    Atlas Choice <input type="checkbox"  data-ng-model="search.company" ng-true-value="Atlas Choice" ng-false-value="" value="Atlas Choice" /><br>
    Holiday Autos <input type="checkbox"  data-ng-model="search.company" ng-true-value="Holiday Autos" ng-false-value="" value="Holiday Autos" /><br>
    Avis <input type="checkbox"  data-ng-model="search.company" ng-true-value="Avis" ng-false-value="" value="Avis" /><br>      

Now I want to create a custom filter in my controller, that can iterate over the items in my ng-repeat and return only the items that meet certain criteria - for example, I might create an array of values based on which 'provider' checkboxes are checked, then evaluate each ng-repeat item against that. I just can't work out how I'd do that though, in terms of the syntax - can anyone help?

Here's my Plunker: http://plnkr.co/edit/lNJNYagMC2rszbSOF95k?p=preview

Obey answered 15/5, 2013 at 10:46 Comment(0)
H
168

If you want to run some custom filter logic you can create a function which takes the array element as an argument and returns true or false based on whether it should be in the search results. Then pass it to the filter instruction just like you do with the search object, for example:

JS:

$scope.filterFn = function(car)
{
    // Do some tests

    if(car.carDetails.doors > 2)
    {
        return true; // this will be listed in the results
    }

    return false; // otherwise it won't be within the results
};

HTML:

...
<article data-ng-repeat="result in results | filter:search | filter:filterFn" class="result">
...

As you can see you can chain many filters together, so adding your custom filter function doesn't force you to remove the previous filter using the search object (they will work together seamlessly).

Hurlee answered 15/5, 2013 at 11:32 Comment(10)
Very interesting! The filter can be simplified to return car.carDetails.doors > 2; though.Nullipore
Of course it can. I've decided for a more verbose version, however, to make it more clear as a learning example.Hurlee
In case you need more sophisticated filters, you can also pass your filters more than one argument.Gorki
@mirrimx i didnt get when this function will be called ?Gupton
@Gorki is there any way to pass more then one argument into a filter defined as a controller function, rather then a filter defined as en explicit $filter?Pretoria
@AbrahamP Unfortunately I don't know that.Gorki
Is there a way to match one filter or another. IE if you had 2 custom filters could you match customFilter1 OR customFilter2?Temuco
I like return (car.carDetails.doors > 2);Fretted
When you are working directly with $scope then you can access the search criteria directly through $scope. What is the solution when you are working with the "as vm" option ? in that case 'this' is not the controller.Fourierism
Heck, if we're flexing, function could be inlined as data-ng-repeat="r in results | filter:search | filter:function(r){return r.carDetails.doors>2;}" or smth. But if you want to get called again for future contracts, prefer the readable answer above. There's zero performance gain.Spathose
D
34

If you still want a custom filter you can pass in the search model to the filter:

<article data-ng-repeat="result in results | cartypefilter:search" class="result">

Where definition for the cartypefilter can look like this:

app.filter('cartypefilter', function() {
  return function(items, search) {
    if (!search) {
      return items;
    }

    var carType = search.carType;
    if (!carType || '' === carType) {
      return items;
    }

    return items.filter(function(element, index, array) {
      return element.carType.name === search.carType;
    });

  };
});

http://plnkr.co/edit/kBcUIayO8tQsTTjTA2vO?p=preview

Daggna answered 15/5, 2013 at 11:33 Comment(1)
can you give more than one parameter to a custom filter?Sprig
A
6

You can call more of 1 function filters in the same ng-repeat filter

<article data-ng-repeat="result in results | filter:search() | filter:filterFn()" class="result">
Admittedly answered 11/6, 2014 at 17:56 Comment(2)
possible to OR filters instead of AND?Temuco
mmm... i think the best option is a function to do that, here is an example (not work) plnkr.co/edit/PNwyDKXk9RQcur8Tpp8w?p=preview bestAdmittedly
T
2

One of the easiest ways to fix this is to use the $ which is the search all.

Here is a plunker that shows it working. I have changed the checkboxes to radio ( because I thought they should be complementary )..

http://plnkr.co/edit/dHzvm6hR5P8G4wPuTxoi?p=preview

If you want a very specific way of doing this ( instead of doing a generic search ) you need work with functions in the search.

The documentation is here

http://docs.angularjs.org/api/ng.filter:filter

Thermostatics answered 15/5, 2013 at 11:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.