How to avoid Maximum call stack size exceeded in Observable?

Viewed 1518

I have filtered list:

  public filteredEvents$ = new BehaviorSubject([]);

And method that inverts propery checked_export and pushed changes back:

  public checkAll(): void {
    this.filteredEvents$
      .pipe(
        map((events: IEvent[]) => {
          return events.map((event: IEvent) => {
            event.checked_export = !event.checked_export;
            return event;
          });
        })
      )
      .subscribe((events) => this.filteredEvents$.next(events));
  }

Template is:

<div *ngFor="let item of filteredEvents$ | async">

Obviously I get error:

ERROR RangeError: Maximum call stack size exceeded

I can push chnages to this.selectedEvents$ like:

  public checkAll(): void {
    this.filteredEvents$
      .pipe(
        map((events: IEvent[]) => {
          return events.map((event: IEvent) => {
            event.checked_export = !event.checked_export;
            return event;
          });
        })
      )
      .subscribe((events) => this.selectedEvents$.next(events));
  }

But it calls always this line:

 public export(): void {
    this.eventService.selectedEvents$
      .pipe().subscribe((fileid) => {});
}

My problem is to modify current this.filteredEvents$

1 Answers

The reason you are receiving maximum call stack size exceeded is because you have an infinite loop, you are subscribing to filteredEvents$, then in the subscription you are emitting value to filteredEvents$, which triggers the subscription again, effectively causing an infinite loop.

You can just remove the subscribe, the async pipe automatically subscribe to changes and by using pipe you will be able to transform the data however you want:

  public checkAll(): void {
    this.filteredEvents$ = this.filteredEvents$
      .pipe(
        map((events: IEvent[]) => {
          return events.map((event: IEvent) => {
            event.checked_export = !event.checked_export;
            return event;
          });
        })
      )
  }

As you're using a BehaviorSubject and can't assign an Observable to it, you can subscribe once using take(1), this way it won't be triggered again:

  public checkAll(): void {
    this.filteredEvents$
      .pipe(
       take(1),
        map((events: IEvent[]) => {
          return events.map((event: IEvent) => {
            event.checked_export = !event.checked_export;
            return event;
          });
        })
      )
      .subscribe((events) => this.selectedEvents$.next(events));
  }

The reason this will work is because `take will take the first n number of emissions and then unsubscribe for you.

Related