StackOverflow_23983322: angularjs-checkbox-filter

Illustration of answer to http://stackoverflow.com/questions/23983322/angularjs-checkbox-filter.

by manoj

HTML

<script src="//code.angularjs.org/1.5.7/angular.min.js"></script>
<div ng-controller="WineCtrl as ctrl">
  <div ng-repeat="(prop, ignored) in ctrl.wines[0]" ng-init="ctrl.filter[prop] = {}">
    <h3>{{ prop | capitalizeFirst }}</h3>
    <div class="quarter" ng-repeat="value in ctrl.getValuesFor(prop)">
      <label>
        <input type="checkbox" ng-model="ctrl.filter[prop][value]" />
        {{ value }}
      </label>
    </div>
    <hr />
  </div>
  <div>
    <h3>Available Wines</h3>
    <div ng-repeat="wine in (ctrl.wines | filter:ctrl.filterByProperties) as filteredWines">
      {{ wine.name }} <i>({{ wine.category }})</i>
    </div>
    <hr />
    <b>Number of results: {{ filteredWines.length }}</b>
  </div>
</div>

CSS

.quarter {
  display: inline-block;
  min-width: 25%;
}

JavaScript

(function () {'use strict';

angular.
  module('myApp', []).
  controller('WineCtrl', WineCtrl).
  filter('capitalizeFirst', capitalizeFirstFilter);

// Functions - Definitions
function capitalizeFirstFilter() {
  return function _doFilter(str) {
    return str && (str.charAt(0).toUpperCase() + str.substring(1));
  };
}

function WineCtrl() {
  // Variables - Private
  var self = this;
  
  // Variables - Public
  self.filter = {};
  self.wines = [
    {name: 'Wine A', category: 'red'},
    {name: 'Wine B', category: 'red'},
    {name: 'Wine C', category: 'white'},
    {name: 'Wine D', category: 'red'},
    {name: 'Wine E', category: 'red'},
    {name: 'Wine F', category: 'white'},
    {name: 'Wine G', category: 'champagne'},
    {name: 'Wine H', category: 'champagne'}    
  ];
  
  // Functions - Public
  self.filterByProperties = filterByProperties;
  self.getValuesFor = getValuesFor;
  
  // Functions - Definitions
  function filterByProperties(wine) {
    var activeFilterProps = Object.
      keys(self.filter).
      filter(function (prop) { return !noFilter(self.filter[prop]); });

    // Use this snippet for matching with AND
    return activeFilterProps.
      every(function (prop) { return self.filter[prop][wine[prop]]; });
    // Use this snippet for matching with OR
    //return !activeFilterProps.length || activeFilterProps.
    //  some(function (prop) { return self.filter[prop][wine[prop]]; });
  }
    
  function getValuesFor(prop) {
    return (self.wines || []).
      map(function (wine) { return wine[prop]; }).
      filter(function (value, idx, arr) { return arr.indexOf(value) === idx; });
  }

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

}());