Search code examples
javascriptangulartypescriptangular-materialvirtualscroll

Angular: cdkVirtualFor not rendering new items


I'm building a vertically scrolling calendar. I'm getting the initial days to load, but when new days are added to the list, they aren't being rendered.

<cdk-virtual-scroll-viewport
  class="demo-viewport"
  [itemSize]="100"
  (onContentScrolled)="handleScrollChange($event)"
>
  <calendar-day
    *cdkVirtualFor="let day of days; trackBy: trackByFn"
    [day]="day"
  ></calendar-day>
</cdk-virtual-scroll-viewport>
<button (click)="goToToday()">go</button>

I have a service with a BehaviorSubject updating the days. I know the list of days is being updated, but the change doesn't seem to be detected.

  ngOnInit() {
    this._daysService.days$.subscribe(days => {
      this.days = days;
    })
    this.watchScroll();
    this.handleScrollingUp();
    this.handleScrollingDown();
  }

For more info, the StackBlitz repo is public https://stackblitz.com/edit/material-infinite-calendar


Solution

  • I figured this out.

    Originally, I was adding new days by grabbing the current value like this

    let items = this.items$.value;
    items.push(newItem);
    this.items$.next(items)
    

    Apparently, this is actually a mutation of the value of the BehaviorSubject's value, therefore not creating a new array to return and not triggering change detection.

    I changed it to

    let items = [...this.items$.value];
    items.push(newItem);
    this.items$.next(items)
    

    and all is good.

    So, although the answers here are correct in that I was mutating the original array, the information I needed was calling next() with a mutated version BehaviorSubject's current value does not emit a new array. An emit event does not guarantee immutability.