Search code examples
javascriptangularjsfilterangularjs-ng-repeatangular-ngmodel

Making Angular Filter Dynamic in the controller


I've seen a couple other posts that I thought could help but haven't had any luck. I have a filter that is working fine when I statically specify what property I want to be filtering with. I am building an angular component where I don't know what data will actual be passed into the component. For example, here is some sample data:

this.customers = [
  { name: 'Jim', city: 'Minneapolis', state: 'MN', zip: 44332 },
  { name: 'Boe', city: 'Scottsdale', state: 'AZ', zip: 44332 },
  { name: 'Tom', city: 'S.F.', state: 'CA', zip: 11223 },
  { name: 'Joe', city: 'Dallas', state: 'TX', zip: 34543 },
  { name: 'Jon', city: 'L.A.', state: 'CA', zip: 56433 }
];

My static filters work great:

public filterTextChangeLocal($event: ng.IAngularEvent) {
  if (this.itemDisplayProperty = "name") {
    this.filteredItems = this.$filter("filter")(this.items, {name: this.ngModelValue});
  } else if (this.itemDisplayProperty = "city") {
    this.filteredItems = this.$filter("filter")(this.items, {city: this.ngModelValue});
  } else if (this.itemDisplayProperty = "state") { 
    this.filteredItems = this.$filter("filter")(this.items, {state: this.ngModelValue});
  } else if (this.itemDisplayProperty === "zip") {
    this.filteredItems = this.$filter("filter")(this.items, {zip: this.ngModelValue});
  }
}

The problem is the user of the component may pass in any type of data, with properties that may be completely different so I need something that can account for any property specified. I have an isolate scope property called "itemDisplayProperty" that allows the user to specify which property from their data they want to show in the dropdown, and that is the property that I need to be filtered. They could say, "item-display-property="address", the data in the address property would be displayed in the dropdown. I tried this but doesn't work as multiple "this" words aren't allowed:

this.filteredItems = this.$filter("filter")(this.items, {
  this.itemDisplayProperty : this.ngModelValue
});

Here's my input and dropdown for reference:

<input type="text" class="form-control" 
       ng-change="ctrl.filterTextChangeLocal($event)" 
       ng-model="ctrl.ngModelValue" 
       ng-click="ctrl.openDropdown($event)" />

<ul class="dropdown-menu list-group" ng-if="!ctrl.ngDisabled">
  <li class="list-group-item" 
      ng-repeat="row in ctrl.filteredItems" 
      ng-mousedown="ctrl.onSelectedLocal(row, $event)">

    {{row[ctrl.itemDisplayProperty]}}
  </li>
</ul>

Solution

  • In order to refactor the static filters inside filterTextChangeLocal all that you need to do is construct a $filter expression with a dynamic key.

    This can be achieved by using the so called bracket notation:

    var obj = {};
    obj[myKey] = value;
    

    The refactored filterTextChangeLocal would look something like this:

    public filterTextChangeLocal($event: ng.IAngularEvent) {
        var filterExpression = {};
        filterExpression[this.itemDisplayProperty] = this.ngModelValue;
    
        this.filteredItems = this.$filter("filter")(this.items, filterExpression);
    }