title: AngularJS: Filtering down by location
body: <p>I have an SPA that uses select lists to filter down to a location by region, state/province/EU country, city. </p>
  
  <p>I've got filtering working somewhat, but for my location lat/lng values (which will ultimately be powering a Google Maps widget), it's not working right. I'm not getting filtering beyond Region, and even on Region, I'm getting hits of locations that don't belong to the Region selected.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var app = angular.module('plunker', ['angular.filter']);
  
  app.controller('MainCtrl', function($scope, $anchorScroll, $location, $http) {
  
    $scope.cart = [];
  
    $scope.addToCart = function(index) {
      $scope.cart.push(index);
  
      $scope.cartCount = $scope.cart.length;
    }
  
  
  
    $scope.activeRow = function(index) {
      $scope.selectedRow = index;
  
      $location.hash();
      $anchorScroll('anchor-' + index);
  
    }
  
  
    $scope.gotoAnchor = function(x) {
      var newHash = 'anchor' + x;
  
  
    }
  
    // GET data
          $scope.dataObject = data.List;
          $scope.locationObject = data.Locations;
  
  }).filter('byCountry', function() {
  
    return function(items, location) {
      var filtered = [];
  
      if (!location || !items.length) {
        return items;
      }
  
      items.forEach(function(itemElement, itemIndex) {
        itemElement.Locations.forEach(function(locationElement, locationIndex) {
          if (locationElement.Region === location.Region) {
            filtered.push(itemElement);
            return false;
          }
        });
      });
  
      return filtered;
    };
  }).filter('byRegion', function() {
  
    return function(items, location) {
      var filtered = [];
  
      if (!location || !items.length) {
        return items;
      }
  
      items.forEach(function(itemElement, itemIndex) {
        itemElement.Locations.forEach(function(locationElement, locationIndex) {
          locationElement.Sites.forEach(function(siteElement, siteIndex) {
            if (siteElement.State === location.StateName) {
              filtered.push(itemElement);
              return false;
            }
          });
        });
      });
  
      return filtered;
    };
  }).filter('byCity', function() {
  
    return function(items, location) {
      var filtered = [];
  
      if (!location || !items.length) {
        return items;
      }
  
      items.forEach(function(itemElement, itemIndex) {
        itemElement.Locations.forEach(function(locationElement, locationIndex) {
          locationElement.Sites.forEach(function(siteElement, siteIndex) {
            if (siteElement.City === location.CityName) {
              filtered.push(itemElement);
              return false;
            }
          });
        });
      });
  
      return filtered;
    };
  }).filter('ForMap', function() {
  
    return function(items, location) {
      var filtered = [];
  
      if (!location || !items.length) {
        return items;
      }
  
      items.forEach(function(itemElement, itemIndex) {
        itemElement.Locations.forEach(function(locationElement, locationIndex) {
          if (locationElement.Region === location.Region) {
            filtered.push(itemElement);
            return false;
          }
          locationElement.Sites.forEach(function(siteElement, siteIndex) {
            if (siteElement.State === location.StateName || siteElement.City === location.CityName) {
              filtered.push(itemElement);
              return false;
            }
          });
        });
      });
  
      return filtered;
    };
  
  });</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
    background: #eee;
  }
  div.cart {
    display: block;
    height: 70px;
    background: silver;
    margin-left: 20px;
    width: 200px;
    padding: 5px 10px;
    margin-bottom: 20px;
    margin-top: 20px;
  }
  .cart h1 {
    color: #fff;
    line-height: 20px;
  }
  .item-list-wrapper {
    height: 400px;
    width: 90%;
    border: 1px solid #ddd;
    overflow-y: scroll;
    margin-left: 20px;
  }
  .item-list-wrapper table td {
    padding: 10px;
    vertical-align: middle;
    margin-bottom: 10px;
    font-size: 12px;
  }
  .item-list {
    height: auto;
    width: 100%;
    margin-bottom: 10px;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
    border: 1px solid #fff;
    background: #efefe4;
  }
  .col-num {
    width: 100px;
  }
  .col-compound {
    width: 80px;
  }
  .filters {
    width: 100%;
    clear: both;
    margin-left: 20px;
  }
  .filters select {
    width: 200px;
  }
  .filters column {
    height: 100px;
    width: 200px;
    display: inline-block;
    margin: 0;
    padding: 0;
  }
  .filters select {
    display: inline-block;
  }
  .region {
    font-weight: bolder;
  }
  .state {
    font-weight: normal;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html ng-app="plunker"&gt;
  
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;AngularJS Plunker&lt;/title&gt;
    &lt;link data-require="bootstrap@*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" /&gt;
    &lt;link data-require="bootstrap-css@*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" /&gt;
  
    &lt;link rel="stylesheet" href="angular-ui.min.css" /&gt;
  
    &lt;script&gt;
      document.write('&lt;base href="' + document.location + '" /&gt;');
    &lt;/script&gt;
    &lt;link rel="stylesheet" href="style.css" /&gt;
    &lt;script data-require="angular.js@1.4.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js" data-semver="1.4.7"&gt;&lt;/script&gt;
          &lt;script data-require="angular.js@1.4.x" data-semver="1.4.7" src="https://code.angularjs.org/1.4.7/angular-messages.js"&gt;&lt;/script&gt;
          &lt;script data-require="ui-bootstrap@*" data-semver="0.13.3" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.1/ui-bootstrap.min.js"&gt;&lt;/script&gt;
          &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.7/angular-filter.min.js"&gt;&lt;/script&gt;
          &lt;script src="angular-ui.min.js"&gt;&lt;/script&gt;
          &lt;script src="app.js"&gt;&lt;/script&gt;
          &lt;script src="http://zbl.me/test/103015.js"&gt;&lt;/script&gt;
  
  &lt;/head&gt;
  
  &lt;body ng-controller="MainCtrl"&gt;
    &lt;div ng-view=""&gt;&lt;/div&gt;
  
  
  
    &lt;!--item-list-wrapper --&gt;
  
    &lt;div class="filters"&gt;
      &lt;h2&gt;Filter results&lt;/h2&gt;
      &lt;column&gt;
        &lt;select name="selectRegion" class="form-control" ng-model="selectRegion" ng-change="europeSelected()" ng-options="location as location.Region for location in locationObject | orderBy: location.Region:reverse"&gt;
          &lt;option value=""&gt;Select Region&lt;/option&gt;
        &lt;/select&gt;
  
        &lt;select name="selectState" class="form-control" ng-disabled="!selectRegion" ng-model="selectState" ng-options="state as state.StateName for state in selectRegion.States"&gt;
          &lt;option value=""&gt;Select State/Province/Country&lt;/option&gt;
        &lt;/select&gt;
  
        &lt;select name="selectCity" class="form-control" ng-disabled="!selectState" ng-model="selectCity" ng-options="city as city.CityName for city in selectState.Cities"&gt;
          &lt;option value=""&gt;Select City&lt;/option&gt;
        &lt;/select&gt;
      &lt;/column&gt;
      &lt;column&gt;
        &lt;select name="selectPhase" class="form-control" ng-model="selectPhase" ng-options="data.Phase as data.Phase for data in dataObject | unique: 'Phase' | orderBy: 'Phase' "&gt;
          &lt;option value=""&gt;Select Phase&lt;/option&gt;
        &lt;/select&gt;
        &lt;select name="selectNumber" class="form-control" ng-model="selectNumber" ng-options="data.Number as data.Number for data in dataObject | unique: 'Compound' | orderBy: 'Compound' "&gt;
          &lt;option value=""&gt;Select Number&lt;/option&gt;
        &lt;/select&gt;
      &lt;/column&gt;
  
  
    &lt;/div&gt;
  
  
  
    &lt;div class="cart"&gt;
      &lt;h1&gt;Cart: {{cartCount}}&lt;/h1&gt;
    &lt;/div&gt;
  
  
    &lt;div class="item-list-wrapper"&gt;
      &lt;table class="table table-condensed table-hover"&gt;
        &lt;tr ng-repeat="data in dataObject | byCountry : selectRegion | byRegion : selectState | byCity : selectCity | filterBy:['Phase']: selectPhase | filterBy:['Number']: selectNumber track by $index" ng-click="activeRow($index)"&gt;
          &lt;td class="column"&gt;{{data.Phase}}&lt;/td&gt;
          &lt;td class="column col-num"&gt;{{data.Number}}&lt;/td&gt;
          &lt;td class="column col-compound"&gt;{{data.Compound}}&lt;/td&gt;
          &lt;td&gt;
            &lt;span ng-repeat="location in data.Locations track by $index" class="region"&gt;{{ location.Region}}: 
        &lt;span ng-repeat="site in location.Sites | unique: 'State'" class="state"&gt;{{site.State}}
       &lt;/span&gt;
            &lt;/span&gt;
          &lt;/td&gt;
          &lt;td&gt;&lt;a href="" ng-click="addToCart()"&gt;Add&lt;/a&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;
  
    &lt;!-- lat lng debugging --&gt;
    &lt;div style="width: 100%; height: 400px; position: absolute; bottom: 0; left: 0; background: rgba(255,255,255,0.9); z-index: 1000; padding: 10px; overflow-y:scroll; border-left: 5px solid black; border-right: 5px solid black"&gt;
      &lt;h3&gt;filtered lat/lng values&lt;/h3&gt;
  
      &lt;span ng-repeat="data in dataObject | ForMap : selectRegion | ForMap : selectState | ForMap : selectCity "&gt;
  
       &lt;span class="trials-item-cell"&gt;
        &lt;span ng-repeat="location in data.Locations track by $index" class="region"&gt;
                    &lt;span ng-repeat="site in location.Sites | unique: 'State'" class="state"&gt;{{site.City}}: [{{site.Latitude}},{{site.Longitude}}]&lt;span ng-show=" ! $last "&gt;, &lt;/span&gt;
      &lt;/span&gt;
      &lt;/span&gt;
      &lt;/span&gt;
  
  
      &lt;/span&gt;
  
    &lt;/div&gt;
  
  
  
  &lt;/body&gt;
  
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Plunkr: <a href="http://plnkr.co/edit/LKcFhG?p=preview" rel="nofollow">http://plnkr.co/edit/LKcFhG?p=preview</a></p>