I am really surprised there is no documentation anywhere or questions regarding this more advanced filtering mechanism for AngularJS - it is essential for any UI.
I need to filter out select options that include the current item. The question is how can I splice out each options as we iterate through the items coming to our filter, and return the proper results for each select in the list being filtered.
for instance, if we have three regions - Canada, United Kingdom, United States - the select input should read like this:
Canada
[select]
[option0] United Kingdom
[option1] United States
United Kingdom
[select]
[option0] Canada
[option1] United States
United States
[select]
[option0] Canada
[option1] United Kingdom
.. and so on...
HTML:
<div ng-repeat="region in data.regions">
<h2> {{region.name}} </h2>
<input ui-select2="version2" type="hidden" name="keywordsLocal-{{$index}}" class="region-keywords input-xlarge" data-ng-model="data.regions[$index].keywords" required-multiple />
<select ui-select2 id="copy-{{$index}}" ng-show="region.length > 1" class="input-xlarge" ng-click="_copy($event, $index)" data-ng-model="data.regions[$index].keywords">
<option value="">Use the same keywords as:</option>
<option ng-repeat="region in data.regions | myFilter" value="{{region.keywords}}">{{region.name}}</option>
</select>
</div>
Javascript:
app.filter('myFilter', function() {
return function(items) {
var group = [];
for (var i = 0; i < items.length; i++) {
group.push(items.name);
}
for (var i = 0; i < group.length; i++) {
group[i].splice(i, 1);
}
return group;
};
});
There is a flaw in making a select not include what is currently selected. I'll include the bug in the solution so you can see it.
ng-options is what you need. Here is an example using 2 separate selects.
<select ng-model='selectedCountry' ng-options='country.name for country in countries'></select>
<select ng-model='selectedCountry' ng-options='country.name for country in filteredCountries()'></select>
Here is the $scope
pieces. Notice that the filteredCountries is just returning countries, minus the selected country.
$scope.countries = [{name:'United States'},{name:'United Kingdom'},{name:'Canada'}];
$scope.filteredCountries = function(){
var filteredCountries = [];
angular.forEach($scope.countries, function(val, key){
if(val !== $scope.selectedCountry){
this.push(val);
}
},filteredCountries);
return filteredCountries;
};
The bug you'll notice is that as soon as you select the second one, it doesn't show your selected value, since it is immediately filtered out of the options. That isn't the best user experience.
Working example: http://plnkr.co/edit/cRBdkE3akCeBXYqfyHFO
Update: Since you can't use ng-options with the AngularUI Select2 directive here is a plunkr with a select 2 example: http://plnkr.co/edit/Hmt1OXYvtHOAZPzW7YK3?p=preview
Also instead of the filteredCountries function as listed above, a watch
would be more efficient. It will only run the code when it detects the watched value changing.
$scope.$watch('version1model',function(){
$scope.filteredItems = [];
angular.forEach($scope.items, function(val, key){
if(val.id != $scope.version1model){
this.push(val);
}
},$scope.filteredItems);
});
I came up with a solution to this. it is a basic filter, but I actually pass the current value of the index in my ng-repeat to the filter:
Filter:
app.filter('keywordFilter', function() {
return function(items, name) {
var arrayToReturn = [];
for (var i = 0; i < items.length; i++) {
if (items[i].name != name.name) {
arrayToReturn.push(items[i]);
}
}
return arrayToReturn;
};
});
HTML
<select ui-select2 id="copy-{{$index}}" class="input-xlarge" ng-change="_copy($index)" ng-options="region.name for region in data.regions | keywordFilter: {name: region.name}" data-ng-model="selectedKeywords">
<option value="">Use same keywords as:</option>
</select>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With