Jasper Kent
Jasper Kent

Reputation: 3676

Angular Looping Over Iterator

I have a component with the method:

public *numGen(): Iterator<number> {
    for (let i = 0; i < 5; ++i)
      yield i;
  }

and HTML with:

<p *ngFor="let n of numGen()">{{n}}</p>

I expect this to generate:

<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>

which it does, but it also generates an error:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. 
Previous value: 'ngForOf: [object Generator]'. Current value: 'ngForOf: [object Generator]'.

Anyone know how to prevent the error being generated?

Upvotes: 2

Views: 254

Answers (1)

Poul Kruijt
Poul Kruijt

Reputation: 71911

One way is to use the OnPush change detection strategy. This will make sure it only checks the bindings when something inside the component has changed by @Input, or an event emitted from the template. Which by itself has a big performance improvement, and a more logical pattern of when change detection is triggered.

@Component({
  // ...
  changeDetection: ChangeDetectionStrategy.OnPush
})

see here

The reason you are seeing this error, is because your method returns a new Generator object reference with every change detection.


Another way would be to only set the generator when something changes in your component that requires the generator to update. You could use the ngDoCheck hook for that. Be aware that this get called quite a lot, although not as often if you use the OnPush strategy, which I anyways strongly suggest.

export class AppComponent  {
  generator: Iterable<number> = this.numGen();

  multiplier = 1;

  ngDoCheck(): void {
    this.generator = this.numGen();
  }

  times(): void {
    this.multiplier *= 2;
  }

  private *numGen(): Iterable<number> {
    for (let i = 0; i < 5; ++i) {     
      yield i * this.multiplier;
    }
  }
}

example with ngDoCheck

I do wonder, for what use case are you using the iterator/generator?

Upvotes: 2

Related Questions