Skip to content

Commit f3ce4d8

Browse files
committed
perf(cdk/a11y): avoid triggering change detection if there are no subscribers to stream
Currently we have an `NgZone.run` call on each `focus` and `blur` event of a monitored event in order to bring its subscribers into the `NgZone`, however this means that we're also triggering change detection to any consumers that aren't subscribed to changes (e.g. `mat-button` only cares about the classes being applied). These changes move around some logic so that the `NgZone.run` is only hit if somebody has subscribed to the observable.
1 parent 71b7b15 commit f3ce4d8

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

src/cdk/a11y/focus-monitor/focus-monitor.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
Output,
2121
AfterViewInit,
2222
} from '@angular/core';
23-
import {Observable, of as observableOf, Subject, Subscription} from 'rxjs';
23+
import {Observable, of as observableOf, Subject, Subscription, Observer} from 'rxjs';
2424
import {coerceElement} from '@angular/cdk/coercion';
2525
import {DOCUMENT} from '@angular/common';
2626
import {isFakeMousedownFromScreenReader} from '../fake-mousedown';
@@ -70,6 +70,7 @@ type MonitoredElementInfo = {
7070
checkChildren: boolean,
7171
subject: Subject<FocusOrigin>,
7272
rootNode: HTMLElement|Document
73+
observable: Observable<FocusOrigin>
7374
};
7475

7576
/**
@@ -251,15 +252,28 @@ export class FocusMonitor implements OnDestroy {
251252
}
252253

253254
// Create monitored element info.
255+
const subject = new Subject<FocusOrigin>();
254256
const info: MonitoredElementInfo = {
255257
checkChildren: checkChildren,
256-
subject: new Subject<FocusOrigin>(),
257-
rootNode
258+
subject,
259+
rootNode,
260+
// Note that we want the observable to emit inside the NgZone, however we don't want to
261+
// trigger change detection if nobody has subscribed to it. We do so by creating the
262+
// observable manually.
263+
observable: new Observable((observer: Observer<FocusOrigin>) => {
264+
const subscription = subject.subscribe(origin => {
265+
this._ngZone.run(() => observer.next(origin));
266+
});
267+
268+
return () => {
269+
subscription.unsubscribe();
270+
};
271+
})
258272
};
259273
this._elementInfo.set(nativeElement, info);
260274
this._registerGlobalListeners(info);
261275

262-
return info.subject;
276+
return info.observable;
263277
}
264278

265279
/**
@@ -464,11 +478,7 @@ export class FocusMonitor implements OnDestroy {
464478
}
465479

466480
this._setClasses(element);
467-
this._emitOrigin(elementInfo.subject, null);
468-
}
469-
470-
private _emitOrigin(subject: Subject<FocusOrigin>, origin: FocusOrigin) {
471-
this._ngZone.run(() => subject.next(origin));
481+
elementInfo.subject.next(null);
472482
}
473483

474484
private _registerGlobalListeners(elementInfo: MonitoredElementInfo) {
@@ -550,7 +560,7 @@ export class FocusMonitor implements OnDestroy {
550560
private _originChanged(element: HTMLElement, origin: FocusOrigin,
551561
elementInfo: MonitoredElementInfo) {
552562
this._setClasses(element, origin);
553-
this._emitOrigin(elementInfo.subject, origin);
563+
elementInfo.subject.next(origin);
554564
this._lastFocusOrigin = origin;
555565
}
556566
}

0 commit comments

Comments
 (0)