Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS filter multiple select inputs

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;

    };

});
like image 401
Jeff Voss Avatar asked Mar 06 '13 22:03

Jeff Voss


2 Answers

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); 
  });
like image 135
checketts Avatar answered Oct 04 '22 19:10

checketts


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> 
like image 20
Jeff Voss Avatar answered Oct 04 '22 19:10

Jeff Voss