How to Implement Excel Like Filter in angularjs?

All we need is an easy explanation of the problem, so here it is.

I Need to Implement simple Excel Like Filer for table using angulajs(v.1) I’m getting stuck please help me, I have added my code snippet below. I want to show filtered Data in table after checked checkbox and clicked on OK button. I’m doing this using model but not getting solution, how i can achieve this?

var myApp = angular.module('myApp', [])
myApp.filter('unique', function () {

    return function (arr, field) {
        var o = {}, i, l = arr.length, r = [];
        for (i = 0; i < l; i += 1) {
            o[arr[i][field]] = arr[i];
        }
        for (i in o) {
            r.push(o[i]);
        }
        return r;
    };
})
  
      .controller('employeeController', function ($scope) {
     
   var employees = [{
    "Name": "Alfreds Futterkiste",
    "City": "Berlin",
    "Country": "Germany"
  }, {
    "Name": "Berglunds snabbköp",
    "City": "Luleå",
    "Country": "Sweden"
  }, {
    "Name": "Blauer See Delikatessen",
    "City": "Mannheim",
    "Country": "Germany"
  }, {
    "Name": "Blondel père et fils",
    "City": "Strasbourg",
    "Country": "France"
  }, {
    "Name": "Bólido Comidas preparadas",
    "City": "Madrid",
    "Country": "Spain"
  }, {
    "Name": "Bon app'",
    "City": "Marseille",
    "Country": "France"
  }, {
    "Name": "Bottom-Dollar Marketse",
    "City": "Tsawassen",
    "Country": "Canada"
  }, {
    "Name": "Cactus Comidas para llevar",
    "City": "Buenos Aires",
    "Country": "Argentina"
  }, {
    "Name": "Centro comercial Moctezuma",
    "City": "México D.F.",
    "Country": "France"
  }, {
    "Name": "Chop-suey Chinese",
    "City": "Bern",
    "Country": "Switzerland"
  }, {
    "Name": "Comércio Mineiro",
    "City": "São Paulo",
    "Country": "Canada"
  }];
      $scope.employees=employees;
      
    
})
.changeType {
        background: #eee;
        border-radius: 2px;
        border: 1px solid #bbb;
        color: #bbb;
        font-size: 10px;
        line-height: 9px;
        padding: 4px;
        float: right;
    }

        .changeType:before {
            content: "\25BC ";
        }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script>
 $(document).on('click', '#tblbatch .dropdown-menu', function (e) {
        e.stopPropagation();
    });
    </script>
<body ng-app="myApp">
    <div ng-controller="employeeController">
        <div class="container" style="margin-top:40px;">
            <div class="row">
                {{error}}
                <div class="col-md-6">
                    <table class="table table-bordered table-condensed" >
                        <thead>
                            <tr>
                                <th>Country
                                <div class="dropdown" style="float: right">
                                        <button id="btnSearchBatch" class="changeType dropdown-toggle" ng-click="getAllBatchID()" type="button" data-toggle="dropdown"></button>
                                        <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
                                            <div class="col-md-12" style="padding: 0;">
                                                <table style="width:100%">
                                                    <tr>
                                                        <th style="text-align: left">
                                                            <form class="" role="form">
                                                                Filter by value
                                                                <input id="txtSearch" type="text" class="form-control input-sm" ng-model="batchSearch" placeholder="Search" />
                                                                <div class="row">
                                                                    <div class="col-md-offset-6 col-md-6">
                                                                        <div class="row">
                                                                            <div class="col-md-6" style="padding: 5px 0 0 0"><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" style="font-size:12px">Select All</a></div>
                                                                            <div class="col-md-6" style="padding: 5px 15px 0 5px">
                                                                                <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" style="font-size:12px">Clear</a>
                                                                            </div>
                                                                        </div>
                                                                    </div>
                                                                </div>
                                                                <div class="row" style="border-bottom: 1px dotted #ccc!important;">
                                                                    <div class="col-md-12">
                                                                        <div style="height: 150px; overflow-y: scroll; width: 100%;">
                                                                            <ul style="list-style-type: none; padding: 0">
                                                                                <li ng-repeat="emp in employees | unique: 'Country' | filter: batchSearch">
                                                                                    <input id="chkTableList" type="checkbox" ng-model="search[bat.BatchID]" />&nbsp;<label>{{emp.Country}}</label>
                                                                                </li>

                                                                            </ul>
                                                                        </div>
                                                                    </div>
                                                                </div>
                                                                <div class="row" style="margin-top: 8px;">
                                                                    <div class="col-md-12" style="text-align: center">
                                                                        <input id="btnOk" type="button" value="Ok" ng-click="FilterBatchData(BatchID)" class="btn btn-success btn-sm" style="background-color: #0f9d58; border-color: #18804e; color: #fff; padding: 3px; width: 60px;" />
                                                                        <input id="btnCancel" type="button" value="Cancel" class="btn btn-default btn-sm" style="padding: 3px; width: 60px;" />
                                                                    </div>
                                                                </div>
                                                            </form>
                                                        </th>
                                                    </tr>
                                                </table>
                                            </div>
                                        </div>
                                    </div></th>
                                <th>City</th>
                                <th>Name</th>
                            </tr>
                        </thead>
                        <tbody >
                            <tr ng-repeat="emp in employees">
                                 <td>{{emp.City}}</td>
                                 <td>{{emp.Name}}</td>
                                 <td>{{emp.Country}}</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</body>

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

We want to

  1. Show a list of unique country names with check-boxes to select from.
  2. Filter the country list by counrtySearch text-field
  3. Filter the employees by counrtySearch text-field and selected check-boxes.

Step 1:

First we need to create a mapping of unique country names. We need a list of unique country names (for iteration) and a dictionary of unique country names (for easy access without looping). The following function returns both.

function categorize(arr, field) {
    var o = {}, r = [], i, l = arr.length;
    for (i = 0; i < l; i += 1) {
        if(o[arr[i][field]])continue;
        var _o = {name:arr[i][field], checked:true};
        o[arr[i][field]] = _o;
        r.push(_o);
    }
    return {list:r, dict:o};
};

Step 2:

To filter country-list by countrySearch add a countryFilter and bind ng-model to checkboxes.

<li ng-repeat="country in CountryList.list | filter: countryFilter">
    <input id="chk{{$index}}" type="checkbox" ng-model="country.checked" />&nbsp;
    <label for="chk{{$index}}">{{country.name}}</label>
</li>
$scope.countryFilter = function (country) {
    return $scope.countrySearch.length == 0 ? true : country.name.match(new RegExp($scope.countrySearch,'i'));
};

Step 3:

To filter employees by countrySearch and selected check-boxes add the following filter.

<tr ng-repeat="emp in employees | filter: rowFilter">
    <td>{{emp.Country}}</td>
    <td>{{emp.Name}}</td>
    <td>{{emp.City}}</td>
</tr>
$scope.rowFilter = function (item) {
    return $scope.Countries.dict[item.Country].checked && ($scope.countrySearch.length?item.Country.match(new RegExp($scope.countrySearch,'i')):true);
};

For batch select add this method to scope and call markAll(true) to select all, markAll(false) to select none.

$scope.markAll=(b)=>{
    $scope.Countries.list.forEach(function(x){x.checked=b;});
}

Working Snippet:

var myApp = angular.module('myApp', [])
.controller('employeeController', function ($scope) {
    $scope.countrySearch = "";
    $scope.employees = employees;
    $scope.Countries = categorize(employees, 'Country');
    $scope.countryFilter = function (country) {
        return $scope.countrySearch.length ? country.name.match(new RegExp($scope.countrySearch,'i')):true;
    };
    $scope.rowFilter = function (item) {
        return $scope.Countries.dict[item.Country].checked && ($scope.countrySearch.length?item.Country.match(new RegExp($scope.countrySearch,'i')):true);
    };
    $scope.markAll=(b)=>{
        $scope.Countries.list.forEach(function(x){x.checked=b;});
    }
})

function categorize(arr, field) {
    var o = {}, r = [], i, l = arr.length;
    for (i = 0; i < l; i += 1) {
        if(o[arr[i][field]])continue;
        var _o = {name:arr[i][field], checked:true};
        o[arr[i][field]] = _o;
        r.push(_o);
    }
    return {list:r, dict:o};
};

var employees = [{
    "Name": "Alfreds Futterkiste",
    "City": "Berlin",
    "Country": "Germany"
}, {
    "Name": "Berglunds snabbköp",
    "City": "Luleå",
    "Country": "Sweden"
}, {
    "Name": "Blauer See Delikatessen",
    "City": "Mannheim",
    "Country": "Germany"
}, {
    "Name": "Blondel père et fils",
    "City": "Strasbourg",
    "Country": "France"
}, {
    "Name": "Bólido Comidas preparadas",
    "City": "Madrid",
    "Country": "Spain"
}, {
    "Name": "Bon app'",
    "City": "Marseille",
    "Country": "France"
}, {
    "Name": "Bottom-Dollar Marketse",
    "City": "Tsawassen",
    "Country": "Canada"
}, {
    "Name": "Cactus Comidas para llevar",
    "City": "Buenos Aires",
    "Country": "Argentina"
}, {
    "Name": "Centro comercial Moctezuma",
    "City": "México D.F.",
    "Country": "France"
}, {
    "Name": "Chop-suey Chinese",
    "City": "Bern",
    "Country": "Switzerland"
}, {
    "Name": "Comércio Mineiro",
    "City": "São Paulo",
    "Country": "Canada"
}];
.changeType {
  background: #eee;
  border-radius: 2px;
  border: 1px solid #bbb;
  color: #bbb;
  font-size: 10px;
  line-height: 9px;
  padding: 4px;
  float: right;
}

.changeType:before {
  content: "\25BC ";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script>
  $(document).on('click', '#tblbatch .dropdown-menu', function(e) {
    e.stopPropagation();
  });
</script>

<body ng-app="myApp">
  <div ng-controller="employeeController">
    <div class="container" style="margin-top:40px;">
      <div class="row">
        {{error}}
        <div class="col-md-6">
          <table class="table table-bordered table-condensed">
            <thead>
              <tr>
                <th>Country
                  <div class="dropdown" style="float: right">
                    <button id="btnSearchBatch" class="changeType dropdown-toggle" ng-click="getAllBatchID()" type="button" data-toggle="dropdown"></button>
                    <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
                      <div class="col-md-12" style="padding: 0;">
                        <table style="width:100%">
                          <tr>
                            <th style="text-align: left">
                              <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="pull-right" data-dismiss="modal" style="font-size:12px">Close(X)</a>
                              <form class="" role="form">
                                Filter by value
                                <input type="text" class="form-control input-sm" ng-model="countrySearch" placeholder="Search" />
                                <div class="row">
                                  <div class="col-md-offset-6 col-md-6">
                                    <div class="row">
                                      <div class="col-md-6" style="padding: 5px 15px 0 5px"><a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" ng-click="markAll(true)" style="font-size:12px">Select All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" ng-click="markAll(false)" style="font-size:12px">Select None</a></div>
                                    </div>
                                  </div>
                                </div>
                                <div class="row" style="border-bottom: 1px dotted #ccc!important;">
                                  <div class="col-md-12">
                                    <div style="height: 150px; overflow-y: scroll; width: 100%;">
                                      <ul style="list-style-type: none; padding: 0">
                                        <li ng-repeat="country in Countries.list | filter: countryFilter">
                                          <input id="chk{{$index}}" type="checkbox" ng-model="country.checked" />&nbsp;<label for="chk{{$index}}">{{country.name}}</label>
                                        </li>

                                      </ul>
                                    </div>
                                  </div>
                                </div>
                              </form>
                            </th>
                          </tr>
                        </table>
                      </div>
                    </div>
                  </div>
                </th>
                <th>Name</th>
                <th>City</th>
              </tr>
            </thead>
            <tbody>
              <tr ng-repeat="emp in employees | filter: rowFilter">
                <td>{{emp.Country}}</td>
                <td>{{emp.Name}}</td>
                <td>{{emp.City}}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>
</body>

Modifying it slightly we can apply filter to multiple columns

var myApp = angular.module('myApp', [])
  .controller('employeeController', function($scope) {
    $scope.XLfilters = {
      list: [],
      dict: {}
    };
    $scope.markAll = function(field, b) {
      $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=b;});
    }
    $scope.clearAll = function(field) {
      $scope.XLfilters.dict[field].searchText='';
      $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=true;});
    }
    $scope.itemFilter = function(field) {
      var xfilter = $scope.XLfilters.dict[field];
      if (xfilter.searchText.length == 0) return xfilter.list;
      var rxp = new RegExp(xfilter.searchText, 'i');
      return xfilter.list.filter(function(item) {
        return item.name.match(rxp);
      });
    };
    $scope.rowFilter = function(item) {
      var visible = true;
      for(var cat,i=0,l=$scope.XLfilters.list.length;i<l;i++){
        cat = $scope.XLfilters.list[i];
      	if(!cat.dict[item[cat.field]].checked) return false;
        if(cat.searchText.length){
          if(!item[cat.field].match(new RegExp(cat.searchText,'i'))) return false;
        }
      }
      return true;
    };

    function createXLfilter(arr, field) {
      if ($scope.XLfilters.dict[field]) return;
      var cat = categorize(arr, field);
      cat.field=field;
      cat.searchText = "";
      $scope.XLfilters.dict[field] = cat;
      $scope.XLfilters.list.push(cat);
    }
    $scope.employees = employees;
    createXLfilter(employees, 'Country');
    createXLfilter(employees, 'City');
  })

function categorize(arr, field) {
  var o = {},
    r = [],
    i, l = arr.length;
  for (i = 0; i < l; i += 1) {
    if (o[arr[i][field]]) continue;
    var _o = {name:arr[i][field], checked:true};
    o[arr[i][field]] = _o;
    r.push(_o);
  }
  return {
    list: r,
    dict: o
  };
};

var employees = [{
  "Name": "Manirul Monir",
  "City": "Sylhet",
  "Country": "Bangladesh"
}, {
  "Name": "Arup",
  "City": "Sylhet",
  "Country": "Bangladesh"
}, {
  "Name": "Person 1",
  "City": "Dhaka",
  "Country": "Bangladesh"
}, {
  "Name": "Person 2",
  "City": "Dhaka",
  "Country": "Bangladesh"
}, {
  "Name": "Munim Munna",
  "City": "Narshigdi",
  "Country": "Bangladesh"
}, {
  "Name": "Mahfuz Ahmed",
  "City": "Narshigdi",
  "Country": "Bangladesh"
}, {
  "Name": "Tawkir Ahmed",
  "City": "Gazipur",
  "Country": "Bangladesh"
}, {
  "Name": "Alfreds 2",
  "City": "Berlin",
  "Country": "Germany"
}, {
  "Name": "Alfreds Futterkiste",
  "City": "Berlin",
  "Country": "Germany"
}, {
  "Name": "Blauer See Delikatessen",
  "City": "Mannheim",
  "Country": "Germany"
}, {
  "Name": "Blondel père et fils",
  "City": "Strasbourg",
  "Country": "France"
}, {
  "Name": "Bon app'",
  "City": "Marseille",
  "Country": "France"
}, {
  "Name": "Centro comercial Moctezuma",
  "City": "México D.F.",
  "Country": "France"
}];
.changeType {
  background: #eee;
  border-radius: 2px;
  border: 1px solid #bbb;
  color: #bbb;
  font-size: 10px;
  line-height: 9px;
  padding: 4px;
  float: right;
}

.changeType:before {
  content: "\25BC ";
}

.options {
  height: 150px;
  overflow-y: scroll;
}

a.filterlink{
  font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<div class="col-md-6" ng-app="myApp" ng-controller="employeeController">
  <table class="table table-bordered table-condensed">
    <thead>
      <tr>
        <th>Country
          <div class="dropdown" style="float: right">
            <button id="btnSearchBatch" class="changeType dropdown-toggle" type="button" data-toggle="dropdown"></button>
            <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
              <div class="text-right">
                <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="clearAll('Country')">Clear All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" data-dismiss="modal">Close(X)</a>
              </div>
              <form>
                <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['Country'].searchText" placeholder="Filter by Country" />
                <div>
                  <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('Country',true)">Select All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('Country',false)">Select None</a>
                </div>
                <div class="options">
                  <ul style="list-style-type: none; padding: 0">
                    <li ng-repeat="item in itemFilter('Country')">
                      <input id="countryOption{{$index}}" type="checkbox" ng-model="item.checked" />&nbsp;<label for="countryOption{{$index}}">{{item.name}}</label>
                    </li>
                  </ul>
                </div>
              </form>
            </div>
          </div>
        </th>
        <th>City
          <div class="dropdown" style="float: right">
            <button id="btnSearchBatch" class="changeType dropdown-toggle" type="button" data-toggle="dropdown"></button>
            <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
             <div class="text-right">
               <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="clearAll('City')">Clear All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" data-dismiss="modal">Close(X)</a>
             </div>
             <form>
                <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['City'].searchText" placeholder="Filter by City" />
                <div>
                  <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('City',true)">Select All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('City',false)">Select None</a>
                </div>
                <div class="options">
                  <ul style="list-style-type: none; padding: 0">
                    <li ng-repeat="item in itemFilter('City')">
                      <input id="cityOption{{$index}}" type="checkbox" ng-model="item.checked" />&nbsp;<label for="cityOption{{$index}}">{{item.name}}</label>
                    </li>
                  </ul>
                </div>
              </form>
            </div>
          </div>
        </th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat="emp in employees | filter: rowFilter">
        <td>{{emp.Country}}</td>
        <td>{{emp.City}}</td>
        <td>{{emp.Name}}</td>
      </tr>
    </tbody>
  </table>
</div>

Angular digest cycle optimized multiple column filtering:

var myApp = angular.module('myApp', [])
  .controller('employeeController', function($scope) {
    $scope.XLfilters = { list: [], dict: {}, results: [] };
    $scope.markAll = function(field, b) {
      $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=b;});
    }
    $scope.clearAll = function(field) {
      $scope.XLfilters.dict[field].searchText='';
      $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=true;});
    }
    $scope.XLfiltrate = function() {
    	var i,j,k,selected,blocks,filter,option, data=$scope.XLfilters.all,filters=$scope.XLfilters.list;
      $scope.XLfilters.results=[];
      for (j=0; j<filters.length; j++) {
      	filter=filters[j];
        filter.regex = filter.searchText.length?new RegExp(filter.searchText, 'i'):false;
        for(k=0,selected=0;k<filter.list.length;k++){
        	if(!filter.list[k].checked)selected++;
          filter.list[k].visible=false;
          filter.list[k].match=filter.regex?filter.list[k].title.match(filter.regex):true;
        }
        filter.isActive=filter.searchText.length>0||selected>0;
      }
      for (i=0; i<data.length; i++){
        blocks={allows:[],rejects:[],mismatch:false};
      	for (j=0; j<filters.length; j++) {
          filter=filters[j]; option=filter.dict[data[i][filter.field]];
          (option.checked?blocks.allows:blocks.rejects).push(option);
          if(filter.regex && !option.match) blocks.mismatch=true;
      	}
        if(blocks.rejects.length==1) blocks.rejects[0].visible=true;
        else if(blocks.rejects.length==0&&!blocks.mismatch){
          $scope.XLfilters.results.push(data[i]);
        	blocks.allows.forEach((x)=>{x.visible=true});
        }
      }
      for (j=0; j<filters.length; j++) {
      	filter=filters[j];filter.options=[];
        for(k=0;k<filter.list.length;k++){
          if(filter.list[k].visible && filter.list[k].match) filter.options.push(filter.list[k]);
        }
      }
    }
    function createXLfilters(arr, fields) {
      $scope.XLfilters.all = arr;
      for (var j=0; j<fields.length; j++) $scope.XLfilters.list.push($scope.XLfilters.dict[fields[j]]={list:[],dict:{},field:fields[j],searchText:"",active:false,options:[]});
      for (var i=0,z; i<arr.length; i++) for (j=0; j<fields.length; j++) {
      z=$scope.XLfilters.dict[fields[j]];
      z.dict[arr[i][fields[j]]] || z.list.push(z.dict[arr[i][fields[j]]]={title:arr[i][fields[j]],checked:true, visible:false,match:false});
      }
    }

    createXLfilters(employees, ['Country','City']);
})

var employees = [{
  "Name": "Manirul Monir",
  "City": "Sylhet",
  "Country": "Bangladesh"
}, {
  "Name": "Arup",
  "City": "Sylhet",
  "Country": "Bangladesh"
}, {
  "Name": "Person 1",
  "City": "Dhaka",
  "Country": "Bangladesh"
}, {
  "Name": "Person 2",
  "City": "Dhaka",
  "Country": "Bangladesh"
}, {
  "Name": "Munim Munna",
  "City": "Narshingdi",
  "Country": "Bangladesh"
}, {
  "Name": "Mahfuz Ahmed",
  "City": "Narshingdi",
  "Country": "Bangladesh"
}, {
  "Name": "Tawkir Ahmed",
  "City": "Gazipur",
  "Country": "Bangladesh"
}, {
  "Name": "Alfreds 2",
  "City": "Berlin",
  "Country": "Germany"
}, {
  "Name": "Alfreds Futterkiste",
  "City": "Berlin",
  "Country": "Germany"
}, {
  "Name": "Blauer See Delikatessen",
  "City": "Mannheim",
  "Country": "Germany"
}, {
  "Name": "Blondel père et fils",
  "City": "Strasbourg",
  "Country": "France"
}, {
  "Name": "Bon app'",
  "City": "Marseille",
  "Country": "France"
}, {
  "Name": "Centro comercial Moctezuma",
  "City": "México D.F.",
  "Country": "France"
}];
.filterdropdown{
  background: #eee;
  border: 1px solid #bbb;
  color: #bbb;
  border-radius: 2px;
  font-size: 14px;
  line-height: 14px;
}
.filterdropdown.active{
  color: #005b9c;
}
a.filterlink{
  font-size: 12px;
}
.options {
  height: 150px;
  overflow-y: scroll;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<div class="col-md-6" ng-app="myApp" ng-controller="employeeController">
  <table class="table table-bordered table-condensed" data="{{XLfiltrate()}}">
    <thead>
      <tr>
        <th>Country
          <div class="dropdown" style="float: right">
            <button class="dropdown-toggle filterdropdown" ng-class="{active:XLfilters.dict['Country'].isActive}" type="button" data-toggle="dropdown"><span class="glyphicon glyphicon-filter"></span></button>
            <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
              <div class="text-right">
                <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="clearAll('Country')">Clear All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" data-dismiss="modal">Close(X)</a>
              </div>
              <form>
                <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['Country'].searchText" placeholder="Filter by Country" />
                <div>
                  <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('Country',true)">Select All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('Country',false)">Select None</a>
                </div>
                <div class="options">
                  <ul style="list-style-type: none; padding: 0">
                    <li ng-repeat="item in XLfilters.dict['Country'].options">
                      <input id="countryOption{{$index}}" type="checkbox" ng-model="XLfilters.dict['Country'].dict[item.title].checked" />&nbsp;<label for="countryOption{{$index}}">{{item.title}}</label>
                    </li>
                  </ul>
                </div>
              </form>
            </div>
          </div>
        </th>
        <th>City
          <div class="dropdown" style="float: right">
            <button class="dropdown-toggle filterdropdown" ng-class="{active:XLfilters.dict['City'].isActive}" type="button" data-toggle="dropdown"><span class="glyphicon glyphicon-filter"></span></button>
            <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
              <div class="text-right">
                <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="clearAll('City')">Clear All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" data-dismiss="modal">Close(X)</a>
              </div>
              <form>
                <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['City'].searchText" placeholder="Filter by City" />
                <div>
                  <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('City',true)">Select All</a> | <a href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" class="filterlink" ng-click="markAll('City',false)">Select None</a>
                </div>
                <div class="options">
                  <ul style="list-style-type: none; padding: 0">
                    <li ng-repeat="item in XLfilters.dict['City'].options">
                      <input id="cityOption{{$index}}" type="checkbox" ng-model="XLfilters.dict['City'].dict[item.title].checked" />&nbsp;<label for="cityOption{{$index}}">{{item.title}}</label>
                    </li>
                  </ul>
                </div>
              </form>
            </div>
          </div>
        </th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat="emp in XLfilters.results">
        <td>{{emp.Country}}</td>
        <td>{{emp.City}}</td>
        <td>{{emp.Name}}</td>
      </tr>
    </tbody>
  </table>
</div>

Method 2

Change ng-model to have one dummy variable lets say emp.status whose value will be either true or false based on clicking checkbox.

HTML country dropdown

<li ng-repeat="emp in employees | unique: 'Country' | filter: batchSearch">
   <input id="chkTableList" type="checkbox" ng-model="emp.status" ng-click="yourFunction(emp.status, emp.Country)"/>&nbsp;<label>{{emp.Country}}</label>
                                                                        </li>

Controller :

  var countrySet = [];
  $scope.employees=employees;
  $scope.showResults=employees;
  function  _setFilterData(){
      var emplList = angular.copy(employees);

      $scope.showResults = emplList.filter(function(v, idx){
          return countrySet.includes(v.Country)
      });  
  }
  $scope.yourFunction = function(checkBoxStatus, country){
      var index = countrySet.indexOf( country ); 

        if(checkBoxStatus && index === -1 ) {
            countrySet.push( country); 

        }else{
          countrySet.splice( index, 1 );
        }
        _setFilterData();
        console.error(checkBoxStatus, country, countrySet);
    }

Please check plunker for functionality : https://plnkr.co/edit/aXhvM0?p=preview

NOTE: Never mind CSS on plunker

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply