Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(template): remove scroll listener #1563

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

Merged
merged 2 commits into from
May 28, 2023
Merged

fix(template): remove scroll listener #1563

merged 2 commits into from
May 28, 2023

Conversation

arturovt
Copy link
Member

@arturovt arturovt commented May 3, 2023

This requires the same object to be passed back to removeEventListener after calling addEventListener (otherwise it's a leak).

@nx-cloud
Copy link

nx-cloud bot commented May 3, 2023

☁️ Nx Cloud Report

Attention: This version of the Nx Cloud GitHub bot will cease to function on July 1st, 2023. An organization admin can update your integration here.

CI is running/has finished running commands for commit a085ff3. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 6 targets

Sent with 💌 from NxCloud.

@github-actions github-actions bot added </> Template @rx-angular/template related 🔬 Experimental Experimental: Feature, docs, demos labels May 3, 2023
Copy link
Member

@edbzn edbzn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I don't observe any leak on my side, removing the listener without passing the option again works well.

image

Following the removeEventListener doc, it would fail if another option would be passed like { capture: true } but passing nothing means "match any event with this name and this handler".

@hoebbelsB
Copy link
Member

@arturovt thank u for the PR!! Is it possible to elaborate a bit on this? I did memory leak inspections before merging it, so same as @edbzn I cannot observe any problems with the current solution. Thanks for investigating btw :)

@arturovt
Copy link
Member Author

arturovt commented May 8, 2023

The spec defines the following:

Event listeners can be removed by utilizing the removeEventListener() method, passing the same arguments.

Browser engines store event listeners differently on elements, but usually by "joining" listeners and options.

Given the following example:

@Component({
  selector: 'app-root',
  template: `
    <section
      id="scroll"
      #scrollContainer
      style="height: 300px; width: 300px; overflow-y: scroll"
    >
      <p *ngFor="let _ of array">Hey</p>
    </section>
    <button (click)="destroy()"></button>
  `,
})
export class AppComponent {
  array = Array.from({ length: 40 });

  @ViewChild('scrollContainer', { static: true })
  scrollContainer!: ElementRef<HTMLElement>;

  private l = () => {
    console.log(this);
  };

  constructor(private ngModuleRef: NgModuleRef<unknown>) {}

  ngOnInit(): void {
    // @ts-ignore
    this.scrollContainer.nativeElement['__zone_symbol__addEventListener'](
      'scroll',
      this.l,
      { passive: true }
    );
  }

  ngOnDestroy() {
    // @ts-ignore
    this.scrollContainer.nativeElement['__zone_symbol__removeEventListener'](
      'scroll',
      this.l
    );
  }

  destroy() {
    this.ngModuleRef.destroy();
  }
}

If we click the 'destroy' button and save the snapshot in Firefox, we may see the folllowing:

Screenshot from 2023-05-08 16-33-59

Now let's find what captures section with its virtual address 0x7fe639b05900:

$ fxsnapshot 67708.fxsnapshot 'nodes { id: 0x7fe639b05900 }'

CaptureMap {
    lambdas: IdVec(
        [
            LambdaInfo {
                arity: 0,
                parent: None,
                captured: {},
            },
        ],
        PhantomData<fxsnapshot::query::ast::LambdaId>,
    ),
    uses: IdVec(
        [],
        PhantomData<fxsnapshot::query::ast::UseId>,
    ),
}

Which means the event listener still captures the element.

Chrome's Blink also expects the reference to be passed when calling removeEventListener:

bool EventTarget::removeEventListener(const AtomicString &event_type,
                                      const EventListener *listener,
                                      EventListenerOptions *options) {
  return RemoveEventListenerInternal(event_type, listener, options);
}

Firefox Gecko is also checking both listener and options passed:

if (listener->mListener == aListenerHolder &&
    listener->mFlags.EqualsForRemoval(aFlags)) {
  mListeners.RemoveElementAt(i);
}

One of the engines that's doesn't care about options is Safari's WebKit:

bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)

Since it doesn't have a signature where options should be passed when calling removeEventListener. Everything yields to the fact that browsers have different implementations of the spec.

Material is following the same approach and passes options back when removing event listeners: https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-slider/slider.ts#L572-L573

Copy link
Member

@edbzn edbzn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge it, it can only be beneficial. Thank you @arturovt.

@codecov
Copy link

codecov bot commented May 26, 2023

Codecov Report

Merging #1563 (a085ff3) into main (addc3bd) will decrease coverage by 0.05%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##             main    #1563      +/-   ##
==========================================
- Coverage   81.63%   81.58%   -0.05%     
==========================================
  Files         105      104       -1     
  Lines        2232     2227       -5     
  Branches      407      406       -1     
==========================================
- Hits         1822     1817       -5     
  Misses        332      332              
  Partials       78       78              
Flag Coverage Δ
template 86.90% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 3 files with indirect coverage changes

@edbzn edbzn merged commit 944a489 into main May 28, 2023
@edbzn edbzn deleted the fix/rm-scroll branch May 28, 2023 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔬 Experimental Experimental: Feature, docs, demos </> Template @rx-angular/template related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants