22

I have a set of items that I want to filter in ng-repeat using an ng-model as the string to filter the set, so far I haven't found a way to make it work when the expression is negated, I'm doing something like this:

<select ng-model="languageOrigin" ng-change="updatePrice()">
             <option ng-repeat="language in languages">{{language}}</option>
</select>
<select ng-model="languageDestination" ng-change="updatePrice()">
            <option ng-repeat="language in languages | filter:!languageOrigin">{{language}}</option>
</select>

In the documentation, it says that we should use ! to negate the expression but still no luck.

What am I doing wrong?

lgomezma
  • 1,597
  • 2
  • 15
  • 30
  • 1
    If your filter is a method (not string or model) solution is posted here: http://stackoverflow.com/questions/13464809/reverse-polarity-of-an-angular-js-filter/17811582#17811582 – Denis Pshenov Jul 23 '13 at 13:31

6 Answers6

43

'!' character prepend to the filter string, like below:

filter:'!'+languageOrigin

<select ng-model="languageOrigin" ng-change="updatePrice()">
  <option ng-repeat="language in languages">{{language}}</option>
</select>
<select ng-model="languageDestination" ng-change="updatePrice()">
  <option ng-repeat="language in languages | filter:'!'+languageOrigin">{{language}}</option>
</select>
ENDOH takanao
  • 939
  • 8
  • 10
25

If using objects, you might also be interested in the following:

<li data-ng-repeat="obj in objs | filter:({obj_attr: '!obj_val'})">
   ...
</li>

Tested in AngularJS 1.1.5.

dmorlock
  • 1,993
  • 4
  • 18
  • 22
  • 2
    it works when obj_attr contains string values but how should I specify not null? or not ''(empty) string? – Ovidiu Buligan Oct 22 '13 at 13:15
  • 1
    that example did it for me, but beware that if you want to filter based on strings not empty, it will look awful but still work: `filter: {prop: '!' }` where '!' simply translates to 'not empty'. Ugly, but it works :) – Jesper Rønn-Jensen Jun 11 '15 at 13:49
4

UPDATE: see ENDOH takanao's answer.

Looking at the AngularJS source code, it appears that '!' negates the result of the predicate, not the predicate itself:

var search = function(obj, text){
    if (text.charAt(0) === '!') {
       return !search(obj, text.substr(1));
    }
    switch (typeof obj) {
    ...

So, one way to work around this is to [If you don't like the '!'+myFilter syntax,] you can define your own predicate function in your controller:

$scope.inverseOriginFilter = function(item) {
    return item.search($scope.languageOrigin) == -1
}

Then use it in your HTML:

<select ng-model="languageDestination" ng-change="updatePrice()" 
    ng-options="language for language in languages | filter:inverseOriginFilter">
</select>

Example fiddle.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • that works indeed, but I was wondering if it shouldn't be directly implemented by AngularJS... – lgomezma Nov 08 '12 at 12:26
  • this works fine if your model for each select drop down is the Destination itself, but what if its attached to something else already? – Jeff Voss Mar 06 '13 at 23:08
3

If you're using a method to filter than prefixing the method name with '!' will not work. Instead you can do something like:

// You can register this in your AppCtrl if you like, otherwise just use $scope.
$rootScope.not = function(func) {
    return function (item) { 
        return !func(item); 
    }
};

Then you do:

filter:not(myFilterMethod)

In your html it would look like:

<select ng-model="languageDestination" ng-change="updatePrice()">
  <option ng-repeat="language in languages | filter:not(languageOrigin)">{{language}}</option>
</select>

Reference: https://stackoverflow.com/a/17811582/175830

Community
  • 1
  • 1
Jason Axelson
  • 4,485
  • 4
  • 48
  • 56
1

I'm using 1.3.15 and ENDOH's solution did not work. Instead, filter:'!':languageOrigin worked for me.

Community
  • 1
  • 1
ku-
  • 126
  • 2
  • 3
0

To completely negate the $filter filter, i add this filter:

.filter('$nfilter', ['$filter', function($filter) {
    return function() {
        var itemsToExclude = $filter('filter').apply(null, arguments);
        return arguments[0].filter(x => !itemsToExclude.includes(x));
    }
}])

in template, we can write:

<ui-select-choices repeat="type.id as type in data.types | $nfilter:{id: form.typeId}:true | filter: {label: $select.search}">
    <div ng-bind-html="type.label | highlight: $select.search"></div>
</ui-select-choices>
rhessus
  • 16
  • 2