Rishabh
Rishabh

Reputation: 900

Filter ng-repeat table with multiple filter values for one column

I've generated a dynamic table from array of objects and paginated the rows with angular-ui-bootstrap. I also need to implement row filter functionality in table based on user selections. For example, I would select one or more Cities from check-boxes and only the rows with matching Cities should be shown, or user will select certain names, or a combination of both. Below is sample data:

ctrl.data = [{
  id: 1,
  name: 'Ram Kumar',
  city: 'Delhi',
  date: new Date("October 13, 2014 11:13:00")
}, {
  id: 2,
  name: 'Raman Kumar',
  city: 'Mumbai',
  date: new Date("November 13, 2014 11:13:00")
}, {
  id: 3,
  name: 'Raghav Kumar',
  city: 'Chennai',
  date: new Date("December 13, 2014 11:13:00")
}];

I've also created a metadata which will contain information about all properties of data such as property name, it's unique values, the kind of filter which this property would require i.e. check-box or a slider. Below is the metadata:

ctrl.metadata = [{
  name: "ID",
  values: [1, 2, 3],
  type: "checkbox",
  showFilter: false
}, {
  name: "Name",
  type: "checkbox",
  values: ["Ram Kumar", "Raman Kumar", "Raghav Kumar"],
  showFilter: false
}, {
  name: "City",
  type: "checkbox",
  values: ["Delhi", "Mumbai", "Chennai"],
  showFilter: true
}, {
  name: "Birth Date",
  type: "date",
  showFilter: false
}, {
  name: "Age",
  type: "slider",
  values: [18,26],
  showFilter: false
}];

I've currently made a one-value static filter query variable for some attributes such as ID, Name and City.

<div ng-repeat="attr in metadata">
    <div ng-if="attr.name === 'ID'">
      Enter Filter:
      <input type="textbox" ng-model="search.id" />
    </div>

    <div ng-if="attr.name === 'City'">
      Enter Filter:
      <input type="textbox" ng-model="search.city" />
    </div>
<div>

And I am applying this filter to table rows in this way:

  <tr ng-repeat="row in data | filter: search | pages: currentPage : itemsPerPage">
    <td ng-repeat="item in row">{{ item }}</td>
  </tr>

But firstly, I want the filter query variable (search) to be dynamically generated, as the data and metadata might change. Something like below if possible:

<div ng-repeat="attr in metadata">
    <div ng-if="attr.name === 'ID'">
      Enter Filter:
      <input type="textbox" ng-model="attr.searchQuery" />
    </div>
</div>

I attached the search query to metadata object property called searchQuery. But when I am trying to use it in filter, I see no result:

  <tr ng-repeat="row in data | filter: metadata[$index].searchQuery | pages: vm.currentPage : vm.itemsPerPage">
    <td ng-repeat="item in row">{{ item }}</td>
  </tr>

Secondly, the search query can take only one string, but as I want the filter to be based on more than one value, how can I achieve it? I've researched about custom filter but the problem is, how do I 'record' the selected values, and then send it to the filter (for example, get all the selected cities from check-boxes) ? I have to make every variable dynamic or part of metadata so that it does not break when either number of properties, or name of properties or values are changed. There might be no Cities but States when data is changed, so can't make anything static.

Please suggest some ideas for implementation. Here is a working plunker.

Upvotes: 0

Views: 2955

Answers (1)

developer033
developer033

Reputation: 24874

First you should change your <input> to:

<input type="checkbox" ng-model="vm.selected[$index]" ng-true-value="'{{value}}'" ng-false-value="" /> {{value}}

Then your ng-repeat:

<tr ng-repeat="row in vm.data | filter: vm.search | customFilter: vm.selected | pages: vm.currentPage : vm.itemsPerPage">

And finally you can implement a custom filter to achieve what you want, like this:

.filter('customFilter', function() {
  return function(items, search) {
    if (!search || (Object.keys(JSON.parse(JSON.stringify(search))).length === 0 && search.constructor === Object)) {
      return items;
    }
    var selected = Object.keys(search).map(function(key) {
      return search[key]
    });
    items = items.filter(function(value) {
      return selected.indexOf(value.city) != -1;
    });
    return items;
  }
});

Check the complete code

I hope it helps.

Tip: I don't know if you're using these things of ui-bootstrap in your real project, but they're deprecated, as you can see in console:

PaginationController is now deprecated. Use UibPaginationController instead. pagination is now deprecated. Use uib-pagination instead.

Upvotes: 1

Related Questions