Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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;

    };

});
share|improve this question
add comment

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); 
  });
share|improve this answer
 
Thanks for your answer! I really don't think there is a feasible way to achieve this? atleast not without proper knowledge of the $filter service, the problem is I'm using ui-select2 which doesn't support ng-options, fml! –  whiteb0x Mar 7 at 1:55
 
Thank you again for the update - very cool –  whiteb0x Mar 7 at 23:43
add comment
up vote 1 down vote accepted

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> 
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.