Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Virtual Scroll template caching breaking behavior for Components with ngOnInit #16330

Open
jeanpaulattard opened this issue Jun 19, 2019 · 3 comments
Labels
area: cdk/scrolling P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@jeanpaulattard
Copy link

jeanpaulattard commented Jun 19, 2019

It seems that virtual scroll is not rerunning ngOnInit of template components when template views are being cached. This results in having invalid data if the component performs initial functionality on onInit.

Note that the problem below does not happen if template caching is disabled with templateCacheSize: 0.

I've replicated the issue on this stackblitz: https://stackblitz.com/edit/virtual-scroll-pointer-bug

I have the following scenario where I have a component; in the example, PersonComponent; which is repeated for each element of an array within a virtual scroll vieport (see app.component.html). The PersonComponent takes a Person object as an input, and onInit retrieves an Activity object based on some activity ID from an array of Activities that is cached on a service (ActivityService). The retrieved object is then assigned on a variable within the PersonComponent class.

The problem relies when the end user scrolls down and up in the virtual scroll container. When the elements are destroyed and recreated, it seems that the pointers of the activity variable on the PersonComponent is set to point to another incorrect value.

To replicate the problem,

  • Open the stack blits above
  • Take note of the Names and their associated activity.
  • Scroll down on the virtual scroll container (left scroll), and then all the way up
  • Note that the activities associated with the names have changed.

Note that the content of the activity array is not changing, so it's not an issue of some other process changing the content of the object pointed to. Also if you scroll on a normal non virtual scroll container (right scroll), you can note that the data remains the same and does not change.

See also the animated gif below.

virtual scroll bug

I don't know what might be causing the problem, but it seems that it's caused by the virtual scroll functionality.

I've encounted this problem in v7 but it is also present in v8.

Also note that the values that are passed as an @input() are not changed but remain the same.

Expected Behavior

It should be expected that the data that was retrieved onInit is maintained and rendered correctly.

Environment

  • Angular: v8.0.1
  • CDK/Material: v8.0.1 (also present in v7.3.6)
  • Browser: Chrome
  • Operating System: Ubuntu
@jeanpaulattard jeanpaulattard changed the title Virtual Scroll changing references of variables of rendered component Virtual Scroll template caching breaking behavior with ngOnInit Jun 19, 2019
@jeanpaulattard jeanpaulattard changed the title Virtual Scroll template caching breaking behavior with ngOnInit Virtual Scroll template caching breaking behavior for Components with ngOnInit Jun 19, 2019
@hanishmaram
Copy link

Any update on this. I'm also facing same kind of issue.

@ArtemKlots
Copy link

You've told the phrase When the elements are destroyed and recreated. But element are not destroyed, it is reused

The reason is in your PersonComponent
Angular tries to reuse components and gets it from cache, but instances of you component are not reusable. When angular tries to reuse component in other place - old values are used. It is because activity was set in ngOnInit and there are no way to change it. That's why disabling cache is works for you (Angular creates new instances of the component).

Try to get rid of ngOnInit in PersonComponent OR to get actual data from activityService each time when PersonComponent receive new value through @Input. I made some changes (set actual data on input change) and it works:

export class PersonComponent {

    @Input() set person(person: Person) {
      this.activity = this.activityService.getActivity(person.activityId);
      this._person = person;
    }
    _person: Person;
    activity: Activity;

    constructor(private activityService: ActivityService) {}

}

P.S. Use _person in person.component.html

@mmalerba mmalerba added the needs triage This issue needs to be triaged by the team label May 20, 2020
@mmalerba mmalerba added area: cdk/scrolling P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent and removed needs triage This issue needs to be triaged by the team labels May 28, 2020
@MartinJHammer
Copy link

@ArtemKlots Thanks for this comment. I moved my ngOnInit logic to ngOnChanges and now things works as I expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cdk/scrolling P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests

5 participants