Mike
Mike

Reputation: 41

AngularJS filtering - multiple expressions or dynamically chain ng-filters?

If you take a look at the code below, I'd like to use a text <input> to filter each menu item by multiple ingredients - for example, if the user typed "beef, bacon" into the <input>, the app would return ALL menu items with beef OR bacon as an ingredient.

I'm currently trying to do this using ng-filter, and I'm guessing I need to create a custom filter in order to this. Is this the wrong approach?? Is there a way to dynamically chain filters instead?

Here's some code which should make sense of my problem -

My search model: - Note: using ng-list to turn string into an array of substrings

<div ng-init="searchString=[]">
    <input type="text" ng-model="searchString" ng-list>
</div>

My ng-repeat loop: - Note: using a custom filter to join each of my ingredient into one string

<tr ng-repeat="item in menu | filter:{ category : 'Classics' } | filter:{ ingredients : searchString } ">
    <td class="title">{{ item.title }}</td>
    <td class="ingredients">
        {{ item.ingredients | join:', ' }}
    </td>
    <td class="price">{{ item.price | currency }}</td>
</tr>

My data structure

$scope.menu = [
    {
        "title" : "New Yorker",
        "price" : "4.00",
        "ingredients" : [
            "Salt Beef",
            "Pickles",
            "Mustard"
        ],
        "category" : "Classics"
    },
    {
        "title" : "BLT",
        "price" : "4.00",
        "ingredients" : [
            "Bacon",
            "Lettuce",
            "Tomato"
        ],
        "category" : "Classics"
    }
]

Upvotes: 4

Views: 3769

Answers (2)

GHH
GHH

Reputation: 781

(I get that this is probably a dead question, but I found it too so:)

A custom filter is needed because you want to filter menu items that share at least one ingredient with your search list (i.e. non-empty array intersection). The filter used in the question, filter:{ ingredients : searchString } does not filter that way, nor do any of the other filters built into Angular from their official doc.

Creating a custom filter is easy; Add a new function containsIngredientsFromSearch to the $scope:

 // Filter functions are called separately for each item on the menu
$scope.containsIngredientsFromSearch = function(menuItem){     
  // Check every ingredient on the search list ...
  for(var i in $scope.searchString) {
    var ingredient = $scope.searchString[i];
    // ... does it match an ingredient in menuItem.ingredients?
    if(menuItem.ingredients.indexOf(ingredient) !== -1) {
      // ... if so, stop searching and return true to include this menuItem
      return true;
    }
  }

  // No matching ingredient found? Return false to exclude this item.
  return false;
}

Add the filter to the existing filter chain:

<tr ng-repeat="item in menu | filter:{ category : 'Classics' } | filter:containsIngredientsFromSearch">

See it in action on JSBin.

Upvotes: 2

surui
surui

Reputation: 1542

you can create a custom filter, or use a angular filter with a predicate (a function)

{ filter_expression | filter:predicateFunction }}

of course, your function lives in your controller's scope, where the search string is visible

A predicate function can be used to write arbitrary filters. The function is called for each element of array. The final result is an array of those elements that the predicate returned true for.

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

Upvotes: 1

Related Questions