4

I am trying to apply a filter using checkboxes.

The checkboxes are shown correctly:

<div data-ng-repeat="cust in customers">
    <input type="checkbox" data-ng-model="search.city" data-ng-true-value="{{ cust.city }}" data-ng-false-value=""/> {{ cust.city }}
</div>

but when checking any checkbox, nothing happens:

<table>

    <!-- table heading goes here -->

    <tbody>
        <tr data-ng-repeat="customer in customers | filter : search">
            <td >
                {{ customer.firstName }}
            </td>
            <td >
                {{ customer.lastName }}
            </td>
            <td >
                {{ customer.address }}
            </td>
            <td >
                {{ customer.city }}
            </td>
        </tr>
    </tbody>
</table>

The table shows all the customers.

What I want to achieve is: when one or more checkboxes are checked, the table has to show only these rows which match the condition of the checked checkboxes.

What do I have to do to get this working?

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
Martijn
  • 24,441
  • 60
  • 174
  • 261

2 Answers2

7

You can pass a function to the AngularJS filter. For example:

Set you input tag as:

<input type="checkbox" ng-model="search[cust.city]" /> {{ cust.city }}

Set your filter as:

<tr data-ng-repeat="customer in customers | filter:searchBy() ">

In your controller:

function ctrl($scope) {
  $scope.customers = [...];

  $scope.search = {};    
  $scope.searchBy = function () {
    return function (customer) {
      if ( $scope.search[customer.city] === true ) {
        return true;
      }
    }
  };
}

If you wish to show all customer at startup, simply initialise $scope.search with city from the customers array.

Here is a sample Plunker.

marcoseu
  • 3,892
  • 2
  • 16
  • 35
  • I like this answer much better. It appears that it would be faster as well. – fauverism Dec 17 '14 at 16:29
  • 1
    "If you wish to show all customer at startup, simply initialise $scope.search with city from the customers array. " Can you please explain this a bit more. any example for this ?? – Neeraj Sharma Aug 25 '16 at 10:48
  • This solution does not group checkboxes if 2 or more customers belongs to the same city – llermaly Dec 22 '16 at 18:34
3

It looks like you are providing a list of customers, and when one or more is selected, display a table of customers that are in the same city as those customers that are selected.

To do this, you will need a custom filter:

// Define our filter
app.filter('selectedCustomerCities', function($filter) {
  return function(customers) {
    var i, len;

    // get customers that have been checked
    var checkedCustomers = $filter('filter')(customers, {checked: true});

    // Add in a check to see if any customers were selected. If none, return 
    // them all without filters
    if(checkedCustomers.length == 0) {
      return customers;
    }

    // get all the unique cities that come from these checked customers
    var cities = {};
    for(i = 0, len = checkedCustomers.length; i < len; ++i) {
      // if this checked customers cities isn't already in the cities object 
      // add it
      if(!cities.hasOwnProperty(checkedCustomers[i].city)) {
        cities[checkedCustomers[i].city] = true;
      }
    }

    // Now that we have the cities that come from the checked customers, we can
    //get all customers from those cities and return them
    var ret = [];
    for(i = 0, len = customers.length; i < len; ++i) {
      // If this customer's city exists in the cities object, add it to the 
      // return array
      if(cities[customers[i].city]) {
        ret.push(customers[i]);
      } 
    }

    // we have our result!
    return ret;
  };
});

Your markup will then change into something like this:

<div data-ng-repeat="customer in customers">
  <!-- record that this customer has been selected -->
  <input type="checkbox" ng-checked="customer.checked" ng-model="customer.checked" /> {{ customer.city }}
</div>

<table>
  <!-- table heading goes here -->
  <tbody>
      <!-- use our custom filter to only display customers from the cities selected -->
      <tr data-ng-repeat="customer in customers | selectedCustomerCities">
          <td>{{ customer.firstName }}</td>
          <td>{{ customer.lastName }}</td>
          <td>{{ customer.address }}</td>
          <td>{{ customer.city }}</td>
      </tr>
  </tbody>
</table>

You can see it working at this Plunker: http://plnkr.co/edit/GlHRLKECR4jeBS7Vf7TX?p=preview

Jay
  • 18,959
  • 11
  • 53
  • 72
  • This is almost what I want. By default all the customers are displayed. When a checkbox is checked for a certain city (or cities), the filter is applied. – Martijn May 12 '13 at 18:58
  • I've updated my answer. The change was trivial: `if(checkedCustomers.length == 0) { return customers; }` basically returning all the customers if none were checked. Hopefully that all makes sense to you & let me know if you need anything explained. – Jay May 12 '13 at 21:24
  • That's exactly what I want. I understand almost everyhtinh, except this `$filter('filter')(customers, {checked: true});` Could you explain this a little bit more? – Martijn May 13 '13 at 09:06
  • In AnguarJS, you can use the [$filter service](http://docs.angularjs.org/api/ng.$filter) in either the HTML Templates, or in the JavaScript itself. AngularJS provides a set of filters themselves such as [currency](http://docs.angularjs.org/api/ng.filter:currency), [lowercase](http://docs.angularjs.org/api/ng.filter:lowercase), etc for conveinence sake. They also provide the filter "[filter](http://docs.angularjs.org/api/ng.filter:filter)" which is what I am using here in the JavaScript. Read the documentation for the filter service + provided filter "filter" for more information. – Jay May 13 '13 at 14:55
  • 1
    to avoid `cannot read property 'length' of undefined` error if your data is coming from web api, you've to check `if (!angular.isUndefined(customers))` – Ravimallya Oct 13 '16 at 06:32