ngGrid Multi Column Filtering

Chris Haines picture Chris Haines · May 31, 2013 · Viewed 17.5k times · Source

I am using the ngGrid module for AngularJS to show some paged data. I want to be able to search across multiple columns, however using an OR search.

Lets say I have a column with the following headings: Id, Name, Description. When I search I want to return all rows where either Id OR name OR description contain the search term.

    $scope.pagingOptions = {
        pageSizes: [20, 50, 100],
        pageSize: 20,
        totalServerItems: 0,
        currentPage: 1
    };

    $scope.gridOptions =
        {
            data: 'myData',
            columnDefs: [
                { field: 'id', displayName: 'Id' },
                { field: 'name', displayName: 'Name' },
                { field: 'description', displayName: 'Description' },
                { displayName: 'Actions', cellTemplate: '<input type="button" data-ng-click="doSomething(row.entity)" value="Do Something" />'}],
            enablePaging: true,
            showFooter: true,
            showFilter: true,
            pagingOptions: $scope.pagingOptions,
            filterOptions: {
                filterText: "",
                useExternalFilter: false
            }
        };

I have tried using the default search box, and also using an external input box bound to $scope.filterText to define a custom filter such as:

$scope.filterUpdated = function () {
    $scope.gridOptions.filterOptions.filterText = 'id:' + $scope.filterText + ';name:' + $scope.filterText + ';description:' + $scope.filterText;
};

However this seems to do an AND on all of the columns. Is it possible to achieve what I want using the ngGrid module?

Thanks in advance,

Chris

Answer

Gilad Peleg picture Gilad Peleg · Aug 24, 2013

Yes it's possible to do an OR filter, but after searching in the ng-grid source code I can't see how it can be done using their filterOptions.filterText. That can do AND filtering only.

The solution would be then to use filterOptions.useExternalFilter:true

I also found no examples of it, but after playing around with that for a bit, I got the notion that the filter is actually done by re-creating the gridOptions.data object|array. That is the only downside to this filter.

Plunker code is here

So basically your code would look like this index.html:

<body ng-controller="MyCtrl">
  <strong>Filter Name:</strong> </string><input type="text" ng-model="filterName"/>
  </br>
  OR
  </br>
  <strong>Filter Age:</strong> </string><input type="text" ng-model="filterAge"/>
  </br>
  <button ng-click="activateFilter()">Run Filter</button>
  <br/>
  <br/>
  <div class="gridStyle" ng-grid="gridOptions"></div>
</body>

And in your controller.js:

app.controller('MyCtrl', function($scope) {

$scope.filterOptions = {
  filterText: '',
  useExternalFilter: true
};

$scope.activateFilter = function() {
  var name = $scope.filterName || null;
  var age = ($scope.filterAge) ? $scope.filterAge.toString() : null;
  if (!name && !age) name='';

  $scope.myData = angular.copy($scope.originalDataSet, []);
  $scope.myData = $scope.myData.filter( function(item) {
    return (item.name.indexOf(name)>-1 || item.age.toString().indexOf(age) > -1);
  });
 };

 $scope.originalDataSet = [{name: "Moroni", age: 50},
               {name: "Tiancum", age: 43},
               {name: "Jacob", age: 27},
               {name: "Nephi", age: 29},
               {name: "Enos", age: 34}];

 $scope.myData = angular.copy($scope.originalDataSet, []);

 $scope.gridOptions = {
  data: 'myData',
  filterOptions:  $scope.filterOptions
 };
});

That's just basic filtering (use regex and/or convert to lowercase for better matching). Also note that if both name and age are empty I set name to be '' and then every element would return true inside the filter (resulting in the whole dataset return).

This option is much better suited to dynamic dataset (read - server fed), but it works just as well but replicating the original dataset and applying the filters on it.