Reputation: 1218
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>
Upvotes: 2
Views: 1358
Reputation: 8980
OK, that's right, with ES6 you can use computed proporties.
With ES5 you could create a filter object like this (with var self=this;
in your controller):
var filterObj = {};
filterObj[self.itemDisplayProperty] = self.ngModelValue;
self.filteredItems = $filter("filter")(self.items, filterObj);
Please have a look at the demo below or this jsfiddle.
Just a note to your variable names. Avoid ng
prefix in your names (e.g. ngDisabled
in your code). Because this variable has nothing to do with AngularJs and a different name will make your code more readable. Something like showDrop
would be better.
angular.module('demoApp', [])
.controller('mainController', MainController)
.directive('dynFilter', DynFilterDirective);
function DynFilterDirective($filter) {
return {
restrict: 'E',
controllerAs: 'ctrl',
templateUrl: 'components/dynFilterTempl.html',
bindToController: {
items: '=',
itemDisplayProperty: '='
},
scope: {},
controller: function() {
var self = this;
self.filterTextChangeLocal = function($event) {
var filterObj = {};
filterObj[self.itemDisplayProperty] = self.ngModelValue;
console.log(filterObj);
self.filteredItems = $filter("filter")(self.items, filterObj);
};
}
}
}
function MainController($filter) {
var vm = this;
vm.items = [
{ 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 },
];
vm.keys = Object.keys(vm.items[0]);
vm.curProp = vm.keys[0];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.js"></script>
<div ng-app="demoApp" ng-controller="mainController as mainCtrl">
{{Object.keys(mainCtrl.items[0])}}
Choose your filter: <select ng-options="prop for prop in mainCtrl.keys" ng-model="mainCtrl.curProp"></select>
<dyn-filter item-display-property="mainCtrl.curProp" items="mainCtrl.items"></dyn-filter>
<script type="text/ng-template" id="components/dynFilterTempl.html">
<input type="text" class="form-control" ng-change="ctrl.filterTextChangeLocal($event)"
ng-model="ctrl.ngModelValue" ng-focus="ctrl.showDrop = true" ng-blur="ctrl.showDrop = false" />
<ul class="dropdown-menu list-group" ng-show="ctrl.showDrop">
<li class="list-group-item" ng-repeat="row in ctrl.filteredItems"
ng-mousedown="ctrl.onSelectedLocal(row, $event)">
{{row[ctrl.itemDisplayProperty]}}
</li>
</ul>
</script>
</div>
Upvotes: 0
Reputation: 1218
I solved my own question this way, appears to be a new ES6 way to use a variable inside an object. The marked answer would have worked as well:
this.filteredItems = this.$filter("filter")(this.items, {[this.itemDisplayProperty] : this.ngModelValue});
Upvotes: 1
Reputation: 7072
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);
}
Upvotes: 3