Skip to content

Memory leak when attaching events via Renderer or Observable.fromEvent #20442

@rkonstantinov

Description

@rkonstantinov

I'm submitting a...


[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

With the Angular 5.0.1 and zone.js 0.8.18, it seems that a reference to the element to which an event is attached is retained even after the event handler is removed and element is removed. This happens if the element is not present initial and after it is shown an event handler is attached to it.

With older (0.8.12) version of the zone.js this behavior cannot be reproduced.

Expected behavior

All of the element references to be released.

Minimal reproduction of the problem with instructions

A ng-cli project to demonstrate the issue can be found

Also a short screencast is available -> https://github.com/rkonstantinov/ng-event-memory-leak/blob/master/scroll-event-leak.webm

and the basic setup:

@Component({
  selector: 'app-root',
  template: `
        <h2>Steps to reproduce</h2>
        <ol>
          <li>Click on the button to show the element - this will attach and instantly detach the scroll event handler</li>
          <li>Click again on the button to hide it</li>
          <li>Open Chrome dev tools and take a Memory snapshot</li>
          <li>Switch to <b>Containment</b></li>
          <li>Inspect <b>Detached DOM tree</b>. There should be one entry which is our div element</li>
        </ol>

        <button (click)="toggle = !toggle">{{toggle ? 'attached' : 'detached'}}</button>
        <div #container *ngIf="toggle" style="background:red; height: 200px; width: 200px"></div>
        `
})
export class AppComponent implements AfterViewInit, OnDestroy {
  private subscription: Subscription;

  toggle = false;

  @ViewChildren('container') containers = new QueryList<any>();

  constructor(private renderer: Renderer2) { }

  ngAfterViewInit() {
    this.subscription = this.containers.changes.filter(x => x.length).subscribe(e => {
      // Observable.fromEvent(e.first.nativeElement, 'scroll').subscribe(() => console.log(42)).unsubscribe();
      this.renderer.listen(e.first.nativeElement, 'click', () => console.log(42))();
    });
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

Environment

Angular version: 5.0.1
zone.js: 0.8.18

Browser:

  • Chrome (desktop) version XX

For Tooling issues:

  • Platform: Mac, Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: zonesIssues related to zone.jsregressionIndicates than the issue relates to something that worked in a previous version

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions