Why don't AngularJS Filters work when inside an ng-if?
Asked Answered
A

1

8

I have a simple AngularJS page with different sections that I show & hide when links are clicked. One of these areas has a repeated list that can be filtered.

When the section containing the list is shows/hidden with ng-show or ng-hide it behaves normally. When ng-if is used, the list cannot be filtered.

Demos


Sample HTML

<nav>
    <a href="javascript:{}" ng-click="area='one';">Area 1</a>
    <a href="javascript:{}" ng-click="area='two';">Area 2</a>
</nav>

<div ng-if="area==='one'">
    <h3>Area 1!</h3>
    <input type="text" placeholder="filter list..." ng-model="filterText" />
    <ul>
       <li ng-repeat="item in list | filter: listFilter">
           {{item.id}} - {{item.name}}
       </li>
    </ul>
</div>

<div ng-if="area==='two'">
    <h3>Area 2!</h3>
    <p>Stuff here...</p>
</div>

Sample Angular

$scope.area="one";
$scope.filterText="";

$scope.list = [
    {id:1, name:"banana"},
    {id:2, name:"apple"},
    {id:3, name:"orange"},
    {id:4, name:"pear"},
    {id:5, name:"apricot"}
];

$scope.listFilter = function(item){
    var term = $scope.filterText.trim().toLowerCase();
    return item.id.toString().indexOf(term) > -1 || item.name.indexOf(term) > -1;
};
Athiste answered 22/1, 2015 at 19:5 Comment(3)
It's not the filter, it's the input (well, the ngModel of the input, anyway). Hence duplicate of: https://mcmap.net/q/125953/-angularjs-ng-model-doesn-39-t-work-inside-ng-if . (To prove this to yourself, you can move the input outside of the ngIf, your example will work as expected). A working modification of yours would use ng-model="$parent.filterText", jsfiddle.net/Lmbfdxvs/10Rupiah
so nesting HTML inside ng-if creates a new scope, but nesting inside ng-show does not?Athiste
Yep. ngIf transcludes the contents, creating a new scope every time it is created (as in, if it becomes false, the scope is destroyed; when it becomes true again a new scope is created). docs.angularjs.org/api/ng/directive/ngIf . nsShow and ngHide basically just toggle/animate classed.Rupiah
K
2

I don't hold a Masters degree on the subject of Prototypal Inheritance myself but I'll try my best to explain it shortly (there's a ton of resources on the subject);

  • Number
  • String
  • Boolean
  • null
  • undefined
  • Symbol (as of ES6)

are considered primitives (MDN).

Now - when you 'inherit' a primitive from your parent scope, what is actually happening is that the child scope 'mirrors' or 'shadows' the given primitive value. As such, you can think of it as a copy of the above.

That is roughly the nature of primitives in the context of Prototypal Inheritance.

This can clearly be observed in a modified version of your broken fiddle.

Try playing with the two inputs and you can see that there is a connection of the two values when you only touch the outside input (i.e. the child value 'shadows' the parent value). But once you touch the inside input, the values disconnect from one another.


The recommended way to get around this is to use a reference to a property on your model (I say model, but really it's just a JS object) that is defined further up the prototype chain;

$parentScope.obj = { filterText: '' };

ng-model="obj.filterText"

Now you should be good to go with ngIf, ngSwitch, ngRepeat to name a few of the angular supplied directives that create a new scope.

Resources on the subject

Kuhl answered 4/11, 2015 at 11:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.