Components: Scrolling SDK (virtual-scroll) - View isn't updated when list is updated (items added/removed)

Created on 25 Dec 2018  路  15Comments  路  Source: angular/components

What is the expected behavior?

If a data-list that binded to <cdk-virtual-scroll-viewport>, is updated (items are added/ removed from this list), the <cdk-virtual-scroll-viewport> updated as well.

What is the current behavior?

Changing the list will not updated the view (<cdk-virtual-scroll-viewport>)

What are the steps to reproduce?

StackBlitz starter: https://stackblitz.com/edit/angular-hsr4tz?file=app%2Fcdk-virtual-scroll-overview-example.html

In this demo you can see that after adding items to the list, the view isn't updated.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 7.1.4
Material 7.2.0
Browser: Chrome

Is there anything else we should know?

The documentation doesn't discuss about adding/removing items from data-list.
See docs at: https://material.angular.io/cdk/scrolling/overview

P3 cdscrolling has pr

Most helpful comment

I don't think this is a bug. Change detection is not intended to be "deep" checking each item in the array. It just checks the reference. Because you are using push, the reference does not change. You should use deconstructing like @elgs mentioned. You can use trackBy to prevent re-rendering the whole list

I'm wondering why a regular *ngFor can detect changes on a push ?
I mean pushing an item in a array then showing the element with a *ngFor is working fine
Shouldn't we expect the same behaviour for the *cdkVirtualFor

All 15 comments

when will this fix be released?

Try this:

add() {
    this.items.push('NEW - ' + this.items.length);
    this.items = [...this.items];
}

https://stackblitz.com/edit/angular-hsr4tz-mz26wq

@elgs - I like your answer, you can even make it one-liner with:
this.items = [...this.items, ('NEW - ' + this.items.length)];

However, you need to remember that "virtual-scroll" directive purpose is to make an extremely long (thousands items or even more) list to be more smooth. But if each time you need to "Add" an item, you regenerate the whole list... then you lose the purpose of this directive, since re-creating the whole list each time you add an item, is very inefficient.

@Gil-Epshtain agreed!

@crisbeto any chance to get any update about this fix? Thanks.

@Gil-Epshtain from my experience, this workaround is actually not bad, I mean, performance wise. The selling point of the virtual scroll is to swap in/swap out rendered items from the backed full item list. With this workaround, the backed full item list got completely redone, but the rendered window (of 50, let's say) of items remains 50. It may no be ideal, but this actually does not affect the selling point of the scroll view. My $.02.

However, you need to remember that "virtual-scroll" directive purpose is to make an extremely long (thousands items or even more) list to be more smooth.

This statement is true.

But if each time you need to "Add" an item, you regenerate the whole list... then you lose the purpose of this directive, since re-creating the whole list each time you add an item, is very inefficient.

No, you missed the point how the scroll view works. The scroll view has two parts: 1) The backing full list of items (could be thousands of), 2) the small amount of rendered items in DOM (like 50 items). The point of the scroll view is to prevent the thousands of items from being poured in the DOM. Copying the references of each items from the old backed list to a new list is actually cheap (though not ideal), the expensive part is rendering DOMs in the browser. In this case, you only redo the backed list, but the number of rendered DOM remain unchanged, so you don't lose the purpose of this directive.

@elgs - I think the best way to test this is with a real world example - once I've implement this - I'll return with conclusions

@Gil-Epshtain you can force a detect changes to update the virtual scroll from ChangeDetectionRef.detectChange(), this method force the view to detect the changes. That can update the virtual-scroll list;

 @Component({
      selector: 'app-component',
      templateUrl: './app-component.html',
      styleUrls: ['./app-component.scss'],
      changeDetection: ChangeDetectionStrategy.OnPush /* This is a strategy to detect changes by 
      demand,  it isn't necessary to your case;*/
 })

 items: AppComponentItems[] = [];

 constructor(changeDetectorRef: ChangeDetectorRef){}

 addItem(){
      items.push(newItem);
      changeDetectorRef.detectChange();
 }

@Gil-Epshtain you can force a detect changes to update the virtual scroll from ChangeDetectionRef.detectChange(), this method force the view to detect the changes. That can update the virtual-scroll list;

 @Component({
      selector: 'app-component',
      templateUrl: './app-component.html',
      styleUrls: ['./app-component.scss'],
      changeDetection: ChangeDetectionStrategy.OnPush /* This is a strategy to detect changes by 
      demand,  it isn't necessary to your case;*/
 })

 items: AppComponentItems[] = [];

 constructor(changeDetectorRef: ChangeDetectorRef){}

 addItem(){
      items.push(newItem);
      changeDetectorRef.detectChange();
 }

Did not work for me.

Did not work for me.

Same for me changeDetectorRef is not working

I don't think this is a bug. Change detection is not intended to be "deep" checking each item in the array. It just checks the reference. Because you are using push, the reference does not change. You should use deconstructing like @elgs mentioned. You can use trackBy to prevent re-rendering the whole list

I don't think this is a bug. Change detection is not intended to be "deep" checking each item in the array. It just checks the reference. Because you are using push, the reference does not change. You should use deconstructing like @elgs mentioned. You can use trackBy to prevent re-rendering the whole list

I'm wondering why a regular *ngFor can detect changes on a push ?
I mean pushing an item in a array then showing the element with a *ngFor is working fine
Shouldn't we expect the same behaviour for the *cdkVirtualFor

Help:: After assigning list as new object, it moves the scrolling position to the top and not retaining the scrolling position.

Is the issue fixed?
when will this be available for us?

this.items = [...this.items, ('NEW - ' + this.items.length)]; worked with me, but how to replace existing object with new one

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Miiekeee picture Miiekeee  路  3Comments

LoganDupont picture LoganDupont  路  3Comments

julianobrasil picture julianobrasil  路  3Comments

vitaly-t picture vitaly-t  路  3Comments

shlomiassaf picture shlomiassaf  路  3Comments