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

Dialog triggers change detection on keyboard events #10143

Closed
OmriSoudry opened this issue Feb 25, 2018 · 13 comments · Fixed by #10160
Closed

Dialog triggers change detection on keyboard events #10143

OmriSoudry opened this issue Feb 25, 2018 · 13 comments · Fixed by #10160
Assignees
Labels
P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@OmriSoudry
Copy link

OmriSoudry commented Feb 25, 2018

Bug, feature request, or proposal:

Opening a dialog using this.dialog.open(...) [where dialog is MatDialog from the constructor] causes my app to run a change detection for every key press - even after closing the dialog. After opening 10 dialogs, each key press triggers 10 change detections. The event triggers are only removed when I refresh the page and reload the app.

This happens even with a dialog that only has a mat-dialog-close button.

What is the expected behavior?

When a dialog is closed (no matter the reason), all listeners should be removed.

What is the current behavior?

The listeners remains, triggering a change detection event after every key press. Trying to hold down a key after a while causes hundreds of change detections every second, and low-end devices freeze.

What are the steps to reproduce?

I Couldn't reproduce it on StackBlitz. I'm using Angular v2.0.0.beta-12.

@crisbeto
Copy link
Member

You're using a very old Angular version which might have the dialog at a point where it was attaching its keyboard events to the document. A lot has changed since then which would've resolved the issue. Can you reproduce it with the latest version of Material and Angular?

@OmriSoudry
Copy link
Author

OmriSoudry commented Feb 26, 2018

Sorry, I'm actually using a newer version than the one used in StackBlitz.
Here's my ng -v output:

Angular CLI: 1.7.1
Node: 8.9.0
OS: win32 x64
Angular: 5.2.6
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cdk: 5.2.2
@angular/cli: 1.7.1
@angular/material: 5.2.2
@angular-devkit/build-optimizer: 0.3.2
@angular-devkit/core: 0.3.2
@angular-devkit/schematics: 0.3.2
@ngtools/json-schema: 1.2.0
@ngtools/webpack: 1.10.1
@schematics/angular: 0.3.2
@schematics/package-update: 0.3.2
typescript: 2.6.2
webpack: 3.11.0

@OmriSoudry
Copy link
Author

OmriSoudry commented Feb 26, 2018

I've tried using Chrome's debug tools to see what happens.
Phase 1
Before a dialog opens. Each keydown event triggers 2 calls for ZoneDelegate.prototype.invokeTask:

  1. a call with the #document element (as applyThis variable) where the active element is "body" with the keydown event (as applyArgs variable).
  2. a call with the "window" element (as applyThis) with no arguments in applyArgs.
    None of the calls triggers a change detection.

Phase 2
The dialog is open. Now, each keydown event triggers the following:

  1. a call with the #document element (as applyThis variable) where the active element is "body" with the keydown event (as applyArgs variable) - same as before. No change detection is triggered.
  2. a call with the "body" element and the keydown event - This triggers a change detection.
  3. another 4 calls where both variables (applyThis & applyArgs) are null.
  4. a call with the "window" element (as applyThis) with no arguments in applyArgs.

Phase 3
The dialog is closed. Now, each keydown event triggers the following:

  1. a call with the #document element (as applyThis variable) where the active element is "body" with the keydown event (as applyArgs variable) - same as before. No change detection is triggered.
  2. a call with the "body" element and the keydown event - This triggers a change detection.
  3. another 3 calls where both variables (applyThis & applyArgs) are null.
  4. a call with the "window" element (as applyThis) with no arguments in applyArgs.

After another 5 dialogs opens and closes, the calls are like so:

  1. a call with the #document element (as applyThis variable) where the active element is "body" with the keydown event (as applyArgs variable) - same as before. No change detection is triggered.
  2. For each dialog that was open, in my case 5 times: - a call with the "body" element and the keydown event - This triggers a change detection, followed by another 3 calls where both variables (applyThis & applyArgs) are null.
  3. a call with the "window" element (as applyThis) with no arguments in applyArgs.

@OmriSoudry
Copy link
Author

The callback that causes the change detection is triggered by FromEventObservable, but I'm not sure who's calling it.

@OmriSoudry
Copy link
Author

Could it be that the dialog's keydownEvents observable is causing this?

@crisbeto
Copy link
Member

We have a global way of handling keyboard events for all overlays which is being used by the dialog. That being said, the global keyboard handler will be removed once there are no more open overlays.

@OmriSoudry
Copy link
Author

The "cdk-overlay-container" div remains even after the dialog is closed. Is that the overlay you're talking about?

@crisbeto
Copy link
Member

@OmriSoudry
Copy link
Author

So what is an open overlay? does the cdk overlay container div is an open overlay? in my case, each dialog adds another event listener... 100 dialogs causes 100 event callbacks for every keydown on tge body element.

@crisbeto
Copy link
Member

Each dialog is an overlay, the overlay container isn't. I'll take a better look later to see if there's anything that could introduce the extra listeners.

@crisbeto crisbeto self-assigned this Feb 26, 2018
@crisbeto crisbeto added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent has pr labels Feb 26, 2018
crisbeto added a commit to crisbeto/material2 that referenced this issue Feb 26, 2018
…tener

Fixes the `OverlayKeyboardDispatcher` not removing the global event listener, even though the subscription gets removed correctly. The issue seems to come from the fact that rxjs attaches the event using `useCapture = true`, however it doesn't pass the `useCapture` parameter when unsubscribing, which leaves listener in place. These changes switch to using `addEventListener` and `removeEventListener` to manage the event.

Fixes angular#10143.
@crisbeto
Copy link
Member

Alright, I could confirm that the event listener wasn't being removed. I've submitted #10160 to address it. As for it running change detection, I think it should still do it while there's an open overlay, because the overlays need to be able to react to changes caused by key presses.

@OmriSoudry
Copy link
Author

OmriSoudry commented Feb 27, 2018

I agree - change detection should be triggered while the dialog is open and receiving events - but the listeners should be removed when the dialog closes. Thank you Kristiyan!

jelbourn pushed a commit that referenced this issue Mar 6, 2018
…tener (#10160)

Fixes the `OverlayKeyboardDispatcher` not removing the global event listener, even though the subscription gets removed correctly. The issue seems to come from the fact that rxjs attaches the event using `useCapture = true`, however it doesn't pass the `useCapture` parameter when unsubscribing, which leaves listener in place. These changes switch to using `addEventListener` and `removeEventListener` to manage the event.

Fixes #10143.
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants