3

I'm writing a simple todo app to learn Angular 2. I noticed that using pipes break the update behavior.

I have the following template

<input type="text" placeholder="Search" #queryBox (keyup)="query=queryBox.value">

<ul class="task-list">
    <task-item *ng-for="#task of tasks | filter:containsString:query" [task]="task" (onDelete)="delete($event)"></task-item>
</ul>

and when I click on the delete button, the underlying data model is changed but this change is not reflected in the UI. I have determined it's because the filter pipe doesn't get called. If I change the above line to

<task-item *ng-for="#task of tasks" [task]="task" (onDelete)="delete($event)"></task-item>

then things like add, delete works again, but obvious I don't get filtering. Why would adding pipes break the update behavior of this binding?

jz87
  • 9,199
  • 10
  • 37
  • 42

2 Answers2

3

That's because by default Pipes are stateless (they render UI just once).

You have to add :

 @Pipe({
  selector: ...,
  pure: false
})

When you set 'pure' to false, Angular updates the data inside the pipe each cycle.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
Romain
  • 801
  • 1
  • 6
  • 13
  • Now I'm confused. I thought Angular 2 is reactive, aren't pipes conceptually functions? My pipe has no internal state. There are 2 parameters, a predicate : (value:any, query:string) => boolean, and query: string. When either is updated, the pipe should re-evaluate. – jz87 Dec 11 '15 at 09:36
  • No, it's not the default behaviour, if you want your pipe to updates data when there's a change, you have to add 'pure: false' in the @Pipe decorator. I think that the Angular team do things that way to save memory and speeds up applications. – Romain Dec 11 '15 at 09:52
  • Although by default Pipes are stateless, they _can_ re-render the UI more than once. E.g., look at the Power Boost Calculator in the [pipes dev guide](https://angular.io/docs/ts/latest/guide/pipes.html). The reason the UI doesn't re-render for the OP is because the delete operation (I assume a `splice()`) doesn't alter the `tasks` reference, so the pipe doesn't see any change to its input. – Mark Rajcok Dec 27 '15 at 05:43
  • You don't need to use `pure: false` for the pipe to re-evaluate and update the UI. See @MarkRajcok answer for the correct usage. Adding `pure: false` can lead to performance issues, don't use this unless you know what you're doing. – Alisson Reinaldo Silva Feb 23 '21 at 21:57
1

By default, pipes are stateless/pure. If their inputs don't change, the pipes are not executed during a change detection cycle. For this question, where we have

"#task of tasks | filter:containsString:query"

tasks, containsString and query are the inputs, and pipe filter is stateless/pure.
tasks is an array (reference).

When a task is deleted, the array reference doesn't change – the tasks input to the pipe doesn't change – hence the stateless/pure pipe is not executed.

If we make the pipe stateful with pure: false, then the pipe will be evaluated on every change detection cycle – even if the inputs do not change.

You may also need to either

  • specify the onPush change detection strategy for the component (if possible), or
  • implement the pipe's transform() method such that it returns the same array (reference).

These alternatives are explained in detail in this answer (I don't want to repeat all of that here).

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492