31

I have created a pipe to be able to use *ngFor with objects. However, when the object is updated, the pipe does not update. I have found a solution here by using stateful pipe with pure: false.

This solution is not acceptable in terms of performance for my use case as it will refresh the pipe for each change (I use this pipe almost everywhere to render complex elements).

Is there a way to trigger a manual refresh of the pipe so it refreshes only when I want?

Here is a plunker - to see the problem try to remove a name by clicking a - button. If you add pure:false you will see that it will work:

@Pipe({name: 'keys', pure: false})
export class Keys implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

What I would like is to add something to my delName() function so it updates the pipe...

Community
  • 1
  • 1
Nate
  • 7,606
  • 23
  • 72
  • 124
  • Do you have to use `Pipe` and are you able to change data structure of `names`? – ulubeyn May 05 '17 at 08:22
  • Have to use `Pipe`, no but I couldn't find a better solution to use object in `*ngFor`. I can't change my data structure... – Nate May 05 '17 at 08:35

3 Answers3

36

First way: paremeter

One way to make a Pure Pipe becoming impure is add a parameter to your Pipe.

when you want to refresh, just change the parameter.


Second way: without parameter

Pure Pipe will be fired when its input has a new instance. So with TypeScript 2.1+, the below code will copy the original object with a new instance, and lead pure pipe to be fired.

    let copy = { ...original }

Negative impact

This will force your component to refresh your pipe every time a variable changes his value, even if this isn't the one used within the pipe contains


Stackblitz

Both ways are included in stackbliz demo.

Raphaël Balet
  • 6,334
  • 6
  • 41
  • 78
Pengyy
  • 37,383
  • 15
  • 83
  • 73
  • It works but it's not very clean... it doesn't make sense to create and pass a variable that will never be used. Is there a cleaner way to achieve that? – Nate May 05 '17 at 08:52
  • 1
    And if you deal with array and ngfor, you may want to use this one: array = array.slice() – Alex Link Oct 18 '19 at 13:32
  • I wouldn't recommend the second way. This will force your component to refresh your pipe every time a variable changes his value, even if this isn't the one used within the pipe contains. (which is the reason why you should use pipe at the first place). I recommend trying other answers – Raphaël Balet Feb 03 '22 at 13:51
5

Create a pipe, here are two way to solve it:

1, but this way will be trigger for many times, which is bad for your website.

   @Pipe({
  name: 'tablepipe',
  pure: false
})

2.Using ngModel + pure pipe

    <input 
                style="position: absolute;
                z-index: -1;"
                [(ngModel)]="detectedChangeRef">
this.detectedChangeRef = Math.random();

your html, because pipe would notice your ref's change, it would trigger your pipe:


   *ngFor="let item1 of item | tablepipe:detectedChangeRef;">

Your pipe

     transform(value,detectedChangeRef) {
    console.log(detectedChangeRef);
     // do sth..
}

Yiki
  • 51
  • 1
  • 3
2

A pure pipe with an object for input will only emit an update if the object's reference changes.

If you can find a way for the object to be replaced with a new instance when part of its structure changes, a pure pipe will automatically update its output as needed.

Assuming your existing delName function just removes one element from the existing structure:

this.elements.remove(0);

you can update it by replacing this.elements with a new version:

this.elements = this.elements.splice(1);
Alex Peters
  • 2,601
  • 1
  • 22
  • 29
  • 2
    Note that `this.elements = this.elements.splice(0, 1);` would assign an array containing only the _deleted_ element. – Christian Jan 20 '20 at 14:55