Mike
Mike

Reputation: 505

AngularJS: After using a select-based filter, it won't clear if limited to a single property

I don't know how to clear a select-based filter, when limited to one property. This is the data:

  $scope.items = [
    {'name': 'Adam', 'fruit': 'apple'},
    {'name': 'Shelley', 'fruit': 'apple'},
    {'name': 'Barbara', 'fruit': 'plum'},
    {'name': 'Steve', 'fruit': 'Adam\'s apple'},
    {'name': 'Marty Appleton', 'fruit': 'onion'}
  ];

This is how I want to filter it, searching for the filter's text in item name property:

    <select ng-model="select1.name" ng-options="item.name as item.name for item in items">
        <option value="">No filter</option>
    </select>

    <div ng-repeat="item in items | filter:select1">
      {{ item.name }}: {{ item.fruit }}
    </div>

When I select one of the options, the list is filtered as expected. But when after that I select the empty "No filter" option again, the list shown is empty. It works perfectly well if I don't limit the filter to one property, i.e. use ng-model="select1" instead of ng-model="select1.name". It also works if I use a text input instead.

Here's a working example of this in action -- you'll notice that you cannot clear the filter with the bottom-right select, but you can with all other elements.

http://codepen.io/anon/pen/vLHqa

I'm lost. What am I doing wrong?

Upvotes: 3

Views: 5600

Answers (3)

twinj
twinj

Reputation: 2049

The accepted answer will not work for situations where you need more than one select filter and will only work in a global way like any.

There is some quirky behaviour when you use ng-options with explicit options. I find that falling back to the simple ng-repeat will work better in some situations.

Instead you should do:

  <select ng-model="select1.name">
    <option value="" selected>No filter</option>
    <option ng-repeat="item in items" ng-value="item.name">{{item.name}}</option>
  </select>

Try it - I forked your CodePen

This way you could have two select filters selecting off two properties.

 <select ng-model="select1.fruit">
    <option value="" selected>No filter</option>
    <option ng-repeat="item in items" ng-value="item.fruit">{{item.fruit}}</option>
  </select>

Upvotes: 0

Brian
Brian

Reputation: 1097

The latest (unstable) versions of Angular introduce a hook for you to drop in a comparator function, which is where I'd LIKE to put what I'm about to show you. But, I'm stuck on v1.0.7, so I had to approach it with the below.

The issue is that even though the user selects an empty string when they select the "empty" option, the property on the object you're filtering against still exists, so Angular's $filter compares each elements' matching property to that empty string, instead of treating that property as removed.

So, I added an ng-change event on my <select /> directive which would delete the property from my filter template object, thereby resetting the filter. Been working for me so far!

Example:

<!-- Assuming select1 is the filter template object, note the ng-change attribute added to your select element -->

<select ng-change="checkForClear( select1 )" ng-model="select1.name" ng-options="item.name as item.name for item in items" >
    <option value="">No filter</option>
</select>

<div ng-repeat="item in items | filter:select1">
  {{ item.name }}: {{ item.fruit }}
</div>


<!-- Then, in your controller -->
<script>
  // Assuming your controller is set up...

  $scope.checkForClear = function( filterTemplate ){
      if( filterTemplate.name == null || filterTemplate.name === '' ){
          delete filterTemplate.name;  // <== THIS removes the property from the template filter and clears the filter for Angular
      }
  }
</script>

Upvotes: 0

Zack Yang
Zack Yang

Reputation: 399

Change to:

<select class="problem" ng-model="select1" ng-options="item.name for item in items">

It's will be work. http://codepen.io/anon/pen/qroJp

Upvotes: 3

Related Questions