Skip to content

Commit 8d3d271

Browse files
committed
refactor(live-announcer): run outside NgZone
Switches to running the timeout from the `LiveAnnouncer` outside the `NgZone`, because it does all of its DOM manipulation directly on the element and it doesn't have to trigger Angular's change detection.
1 parent bcff93e commit 8d3d271

File tree

1 file changed

+14
-9
lines changed

1 file changed

+14
-9
lines changed

src/cdk/a11y/live-announcer/live-announcer.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Optional,
1515
Provider,
1616
SkipSelf,
17+
NgZone,
1718
} from '@angular/core';
1819
import {LIVE_ANNOUNCER_ELEMENT_TOKEN} from './live-announcer-token';
1920

@@ -27,7 +28,8 @@ export class LiveAnnouncer implements OnDestroy {
2728

2829
constructor(
2930
@Optional() @Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN) elementToken: any,
30-
@Inject(DOCUMENT) private _document: any) {
31+
@Inject(DOCUMENT) private _document: any,
32+
private _ngZone: NgZone) {
3133

3234
// We inject the live element as `any` because the constructor signature cannot reference
3335
// browser globals (HTMLElement) on non-browser environments, since having a class decorator
@@ -52,11 +54,13 @@ export class LiveAnnouncer implements OnDestroy {
5254
// - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a
5355
// second time without clearing and then using a non-zero delay.
5456
// (using JAWS 17 at time of this writing).
55-
return new Promise(resolve => {
56-
setTimeout(() => {
57-
this._liveElement.textContent = message;
58-
resolve();
59-
}, 100);
57+
return this._ngZone.runOutsideAngular(() => {
58+
return new Promise(resolve => {
59+
setTimeout(() => {
60+
this._liveElement.textContent = message;
61+
resolve();
62+
}, 100);
63+
});
6064
});
6165
}
6266

@@ -67,7 +71,7 @@ export class LiveAnnouncer implements OnDestroy {
6771
}
6872

6973
private _createLiveElement(): Element {
70-
let liveEl = this._document.createElement('div');
74+
const liveEl = this._document.createElement('div');
7175

7276
liveEl.classList.add('cdk-visually-hidden');
7377
liveEl.setAttribute('aria-atomic', 'true');
@@ -83,8 +87,8 @@ export class LiveAnnouncer implements OnDestroy {
8387

8488
/** @docs-private @deprecated @deletion-target 7.0.0 */
8589
export function LIVE_ANNOUNCER_PROVIDER_FACTORY(
86-
parentDispatcher: LiveAnnouncer, liveElement: any, _document: any) {
87-
return parentDispatcher || new LiveAnnouncer(liveElement, _document);
90+
parentDispatcher: LiveAnnouncer, liveElement: any, _document: any, ngZone: NgZone) {
91+
return parentDispatcher || new LiveAnnouncer(liveElement, _document, ngZone);
8892
}
8993

9094
/** @docs-private @deprecated @deletion-target 7.0.0 */
@@ -95,6 +99,7 @@ export const LIVE_ANNOUNCER_PROVIDER: Provider = {
9599
[new Optional(), new SkipSelf(), LiveAnnouncer],
96100
[new Optional(), new Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)],
97101
DOCUMENT,
102+
NgZone,
98103
],
99104
useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY
100105
};

0 commit comments

Comments
 (0)