AngularJS filter is not working for multiple words from 2 different keys

对着背影说爱祢 提交于 2021-01-27 03:57:46

问题


This is some of sample JSON data.

$scope.Products = [
{
    "Variants": [],
    "SubCategoryID": "66",
    "ProductImagePath": "/images/britannia/887.png",
    "SubCategoryName": "Butter",
    "BrandName": "Britannia",
    "ProductID": "887",
    "BrandID": "76",
    "ProductName": "Butter"
},
{
    "Variants": [],
    "SubCategoryID": "71",
    "ProductImagePath": "/images/amul/886.png",
    "SubCategoryName": "Cheese",
    "BrandName": "Amul",
    "ProductID": "886",
    "BrandID": "47",
    "ProductName": "cheese"
},
{
    "Variants": [],
    "SubCategoryID": "106",
    "ProductImagePath": "/images/amul/885.png",
    "SubCategoryName": "Curd",
    "BrandName": "Amul",
    "ProductID": "885",
    "BrandID": "47",
    "ProductName": "curd"
}
]

And this is How i am rendering to webpage.

<div ng-if="SearchText" class='box' ng-repeat="product in Products | filter:FilterExpr | orderBy:'ProductName'">
    <ng-include src="'commonTemplate.html'"></ng-include>
</div>

There is a search text box in page. When user start typing in serach text box i assigns value to FilterExpr like this.

$scope.$on('SearchTextChanged', function(event, SearchText) {

    if (SearchText.length >=3)  {
        $scope.FilterExpr = SearchText;
    }
});

When user type amul or cheese or butter, It is able to filter the products. Problem is when user types amul curd or curd amul or butter britannia, NO products are displaying on the page.

How to make it work? What change i need to do to so it is able to filter for multiple words.


回答1:


The default comparator used by the default filter is very simple. It looks for the exact searched string and does not parse the search string in any way. The simplest way to improve this is implementing another comparator (doc).

<div ng-if="SearchText" class='box' ng-repeat="product in Products | filter:FilterExpr:searchFuncComparator | orderBy:'ProductName'">
   <ng-include src="'commonTemplate.html'"></ng-include>
</div>

and adding a comparator to the $scope:

$scope.searchFuncComparator = function(actual, expected) {
    // compare actual with expected and return true if match
    // otherwise false
};

All there is left is to implement you're favorite search method above :)




回答2:


You can do this with a custom filter. To create a custom filter, you register a new filter factory function with your module and pass in a name (I chose to call it 'multiFilter'). This factory function returns a filter function, which will automatically receive the input (such as the array from your ng-repeat) as its first value. The subsequent values passed to the filter function are the ones you'll specify in your filter markup.

So, to make our filter nice and reusable, it's going to accept two additional values. The first will be the object keys that we want to search and the second is the search string. So in your html, you would specify this filter as | multiFilter: ['SubCategoryName', 'BrandName'] : SearchText.

Then you'll need to add your custom filter:

.filter('multiFilter', function(){ //factory function
  return function(input, keys, query) { //filter function with input as first argument
    var results = [];
    var terms, key, value, x, i, j;

    if(angular.isDefined(keys) && angular.isDefined(query)) { //1.

      keys = angular.isArray(keys) ? keys : keys.split(' ');  //3.
      terms = query.toLowerCase().split(' ');  //4.

      for (x=0; x < input.length; x++) {  //5.
        for (i=0; i < keys.length; i++) {  //6.
          if (results.indexOf(input[x])===-1) {  //7.
            key = keys[i];
            for (j=0; j < terms.length; j++) {  //8.
              value = input[x][key].toString().toLowerCase();  //9.
              if (value.indexOf(terms[j])!==-1) {  //10.
                  results.push(input[x]);
              }
            }
          }
        }
      }
      return results; //11.

    } else {
      return input;  //2.
    }
  };
});

How it works:

  1. To make sure that there are no errors if either the search string or object keys are empty, we'll check that they are both defined with angular.isDefined().
  2. If either is undefined, we can just return the array that the filter receives.
  3. To make the filter a bit easier to work with, we'll make it so that you can pass either an array of object keys or a single key as a string. If it's a string, we'll convert that to an array with one element using split.
  4. Next, because you want users to be able to search on multiple terms and match any of the search values, we'll split the search string that we get into an array. To make our filter case insensitive, we'll convert the search string (and later the value that we're going to match it to) to lowercase.
  5. In Javascript, native for loops are generally faster than other array iterators, so we're going to use three nested for loops. The outermost loop grabs each object in the input array.
  6. The next loop grabs each key in the array of keys we passed to the filter.
  7. At this point, since we're potentially looping over multiple keys for the same object, we can make the filter a little more efficient by checking if the object we're working with is already in the result set (this way we can skip the inner loop and move on to the next object in the input array).
  8. The innermost loop does the work to loop over each of our search terms and check if they exist in the current input array property value.
  9. Again, to make the filter case insensitive, we'll convert the value of the current input array property to a string and then to lowercase.
  10. If the search term is found, then we push the current input object to the results array.
  11. When the loops are done, we return the results array so that it can used in the repeat or piped to the next filter.

Check the snippet below to see it action:

var app = angular.module('filter.demo', []);

app.controller('ProductsController', ['$scope',
  function($scope) {

    $scope.FilterKeys = ['SubCategoryName', 'BrandName'];

    $scope.Products = [{
      "Variants": [],
      "SubCategoryID": "66",
      "ProductImagePath": "/images/britannia/887.png",
      "SubCategoryName": "Butter",
      "BrandName": "Britannia",
      "ProductID": "887",
      "BrandID": "76",
      "ProductName": "Butter"
    }, {
      "Variants": [],
      "SubCategoryID": "71",
      "ProductImagePath": "/images/amul/886.png",
      "SubCategoryName": "Cheese",
      "BrandName": "Amul",
      "ProductID": "886",
      "BrandID": "47",
      "ProductName": "cheese"
    }, {
      "Variants": [],
      "SubCategoryID": "106",
      "ProductImagePath": "/images/amul/885.png",
      "SubCategoryName": "Curd",
      "BrandName": "Amul",
      "ProductID": "885",
      "BrandID": "47",
      "ProductName": "curd"
    }];



  }
]);

app.filter('multiFilter', function() {
  return function(input, keys, query) {
    var results = [];
    var terms, key, value, x, i, j;

    if (angular.isDefined(keys) && angular.isDefined(query)) {

      keys = angular.isArray(keys) ? keys : keys.split(' ');
      terms = query.toLowerCase().split(' ');

      for (x = 0; x < input.length; x++) {
        for (i = 0; i < keys.length; i++) {
          if (results.indexOf(input[x]) === -1) {
            key = keys[i];
            for (j = 0; j < terms.length; j++) {
              value = input[x][key].toString().toLowerCase();
              if (value.indexOf(terms[j]) !== -1) {

                results.push(input[x]);
              }
            }
          }
        }
      }
      return results;

    } else {
      return input;
    }
  };
});
@import url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css');
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div  class="container">
  <h2>Demo</h2>
  <p><em>use search terms:</em> butter, cheese, amul, britannia, curd</p>
  
  <div ng-app="filter.demo" ng-controller="ProductsController">
    <label>Search:
      <input type="text" name="search" ng-model="SearchText" class="form-control" />
    </label>
    <div ng-if="SearchText" class='box' ng-repeat="product in Products | multiFilter: FilterKeys : SearchText | orderBy:'ProductName' track by $index">
      <h4 class="text-muted">{{product.SubCategoryName}}</h4>
      <p><strong>Brand Name:</strong> {{product.BrandName}}</p>
    </div>
  </div>
  
</div>


来源:https://stackoverflow.com/questions/31189400/angularjs-filter-is-not-working-for-multiple-words-from-2-different-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!