AngularJS checkbox filter

Just to add onto @gkalpak answer, I found this codepen which allows you to provide the total amount left after an option is selected for each category.

Change the ng-repeat from:

<div ng-repeat="wine in (ctrl.wines | filter:ctrl.filterByProperties) as filteredWines">
      {{ wine.name }} <i>({{ wine.category }})</i>
</div>

To

<div ng-repeat="wine in filtered = (ctrl.wines | filter:ctrl.filterByProperties) as filteredWines">
  {{ wine.name }} <i>({{ wine.category }})</i>
</div>

And with the input labels add:

<label>
    <input type="checkbox" ng-model="ctrl.filter[prop][value]" />
    {{ value }}({{(filtered | filter:value:true).length}})
</label>

There are several implementations possible. Here's one:

  1. Have a $scope.filter = {} object to hold the state of each filter. E.g. {red: true, white: false...}.

  2. Associate each checkbox with the corresponding property using ng-model. E.g.: input type="checkbox" ng-model="filter['red']" />.

  3. Have a function (e.g. $scope.filterByCategory(wine)) that decides if a wine should be displayed or not (based on the $scope.filter object).

  4. Use that function to filter the items based on their category. E.g. <div ng-repeat="wine in wines | filter:filterByCategory">


The filterByCategory function could be implemented like this:

function filterByCategory(wine) {
  // Display the wine if
  var displayWine =
      // the wine's category checkbox is checked (`filter[category]` is true)
      $scope.filter[wine.category] ||   // or 

      // no checkbox is checked (all `filter[...]` are false)
      noFilter($scope.filter);

  return displayWine;
};

where noFilter() is a function that checks if there is any filter activated (and returns true if there is none):

function noFilter(filterObj) {
  return Object.
    keys(filterObj).
    every(function (key) { return !filterObj[key]; });
}

See, also, this short demo.


UPDATE:

I created a modified version, which supports multiple filters (not just filtering by category).
Basically, it dynamically detects the available properties (based on the first wine element), adds controls (groups of check-boxes) for applying filters based on each property and features a custom filter function that:

  1. Filters each wine item, based on every property.
  2. If a property has no filter applied (i.e. no check-box checked), it is ignored.
  3. If a property has check-boxes checked, it is used for filtering out wine items (see above).
  4. There is code for applying multiple filters using AND (i.e. all properties must match) or OR (at least one property must match).

See, also, this updated demo.


I prefer use filter as $filter

app.filter('someFilter',checkboxFilter)
checkboxFilter() {
    return function (arr,filter,key,noOne=false) {
        // arr is an array of objects
        // filter is checkbox filter. someting like {1:true,2:false}
        // key is a property in ech object inside arr
        // noOne is a behavior if none of checkbox is activated (default:false)
        if (!arr.length) return null;

        function noOneCheck(filter) {
            return Object.keys(filter).every((key) => {
                return !filter[key]
            })
        }
        return arr.filter((i) => {
            return filter[i[key]] || (noOne && noOneCheck(filter))
        })
    }
};

html:

ng-repeat="u in project.projectTeamInvite | checkbox:project.status:'status' track by $index">