Search code examples
angularjsangular-filtersangularjs-ng-options

ng-options filter based on selected value


I've to write a directive to edit labels for an app.

Each culture (eg: en-US) can have maximum 4 labels per property which are contained into a table :

 $scope.suffixes = ["DisplayName", "Description", "Hint", "Help"];


 <div class="clear" ng-repeat="label in labelModel | filter:cultureFilter">
      <select ng-options="suffix as suffix for suffix in suffixes | filter:suffixFilter(suffix, label.suffix)" ng-model="label.suffix" />

 </div>

and here are my controller functions :

    $scope.cultureFilter = function (label) {
        return (label.culture == $scope.labelState.culture);
    }

    $scope.suffixFilter = function (suffix, selectedValue) {

        //arguments are now : [undefined, "DisplayName"]

        return true; // :-(
    }

So far, I've been able to filter the ng-repeat based on the culture.

But for my ng-options, I have to filter the strings that are already used. But I also have to keep the current value selected. How can I tell my filter that some value shouldn't be filtered because it's the current selected value?

edit

Now, I'm able to receive the selected value, but I can't receive my suffix anymore


Solution

  • You are almost there with using a filter function, but in your case, I think what you really need is a custom filter. They are actually trivial to write.

    A filter is just a factory function registered with the .filter() helper.

    You just need one that takes in the parameters you care about.

    //Outer factory function
    function SuffixFilter(){
    
      //Inner function which is our filter
      return function(items, ignoredItem){
         var filteredItems = [];
    
         angular.forEach(items, function(item){
            //Your filtering logic may be more complex
            // than this. It's only an example
            if(item == someCriteria || item == ignoredItem){
               filteredItems.push(item);
            }
         });
    
         return filteredItems;
      }
    }
    
    angular.module('myModule')
       .filter('suffixFilter', SuffixFilter);
    

    And once you have your suffix filter registered, you can simply call it in your markup with the parameter you care about. Angular handles the rest.

    <!-- Pass in the current selection to ignore -->
    suffixes | suffixFilter:label.suffix
    

    Update - Based on comment below:

    The function parameter doesn't really work that way. It's expecting to evaluate to a function that can be called.

    Technically you could return a function and use closures to accomplish what you want. Something like this:

    <!-- In your markup -->
    filter:suffixFilter(label.suffix)
    
    //In your controller
    $scope.suffixFilter = function (selectedValue) {
    
       //this is your actual filter here
       return function(item){
    
          //You have access to 'selectedValue' via
          // a closure
          return item == someCriteria || item === selectedValue;
       }
    }
    

    So again, while this is certainly possible, I think a custom filter is semantically cleaner.