Skip to content

Commit 6b96ee6

Browse files
committed
perf(focus-monitor): 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 14a51ef commit 6b96ee6

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
@@ -19,7 +19,7 @@ import {
1919
Optional,
2020
Output,
2121
} from '@angular/core';
22-
import {Observable, of as observableOf, Subject, Subscription} from 'rxjs';
22+
import {Observable, of as observableOf, Subject, Subscription, Observer} from 'rxjs';
2323
import {coerceElement} from '@angular/cdk/coercion';
2424
import {DOCUMENT} from '@angular/common';
2525
import {isFakeMousedownFromScreenReader} from '../fake-mousedown';
@@ -69,6 +69,7 @@ type MonitoredElementInfo = {
6969
checkChildren: boolean,
7070
subject: Subject<FocusOrigin>,
7171
rootNode: HTMLElement|Document
72+
observable: Observable<FocusOrigin>
7273
};
7374

7475
/**
@@ -244,15 +245,28 @@ export class FocusMonitor implements OnDestroy {
244245
}
245246

246247
// Create monitored element info.
248+
const subject = new Subject<FocusOrigin>();
247249
const info: MonitoredElementInfo = {
248250
checkChildren: checkChildren,
249-
subject: new Subject<FocusOrigin>(),
250-
rootNode
251+
subject,
252+
rootNode,
253+
// Note that we want the observable to emit inside the NgZone, however we don't want to
254+
// trigger change detection if nobody has subscribed to it. We do so by creating the
255+
// observable manually.
256+
observable: new Observable((observer: Observer<FocusOrigin>) => {
257+
const subscription = subject.subscribe(origin => {
258+
this._ngZone.run(() => observer.next(origin));
259+
});
260+
261+
return () => {
262+
subscription.unsubscribe();
263+
};
264+
})
251265
};
252266
this._elementInfo.set(nativeElement, info);
253267
this._registerGlobalListeners(info);
254268

255-
return info.subject.asObservable();
269+
return info.observable;
256270
}
257271

258272
/**
@@ -433,7 +447,7 @@ export class FocusMonitor implements OnDestroy {
433447

434448
const origin = this._getFocusOrigin(event);
435449
this._setClasses(element, origin);
436-
this._emitOrigin(elementInfo.subject, origin);
450+
elementInfo.subject.next(origin);
437451
this._lastFocusOrigin = origin;
438452
}
439453

@@ -453,11 +467,7 @@ export class FocusMonitor implements OnDestroy {
453467
}
454468

455469
this._setClasses(element);
456-
this._emitOrigin(elementInfo.subject, null);
457-
}
458-
459-
private _emitOrigin(subject: Subject<FocusOrigin>, origin: FocusOrigin) {
460-
this._ngZone.run(() => subject.next(origin));
470+
elementInfo.subject.next(null);
461471
}
462472

463473
private _registerGlobalListeners(elementInfo: MonitoredElementInfo) {

0 commit comments

Comments
 (0)