diff --git a/src/cdk/a11y/focus-monitor/focus-monitor.ts b/src/cdk/a11y/focus-monitor/focus-monitor.ts index 3e42634ec91f..61e440fd4aca 100644 --- a/src/cdk/a11y/focus-monitor/focus-monitor.ts +++ b/src/cdk/a11y/focus-monitor/focus-monitor.ts @@ -19,7 +19,7 @@ import { Optional, Output, } from '@angular/core'; -import {Observable, of as observableOf, Subject, Subscription} from 'rxjs'; +import {Observable, of as observableOf, Subject, Subscription, Observer} from 'rxjs'; import {coerceElement} from '@angular/cdk/coercion'; import {DOCUMENT} from '@angular/common'; import {isFakeMousedownFromScreenReader} from '../fake-mousedown'; @@ -69,6 +69,7 @@ type MonitoredElementInfo = { checkChildren: boolean, subject: Subject, rootNode: HTMLElement|Document + observable: Observable }; /** @@ -244,15 +245,28 @@ export class FocusMonitor implements OnDestroy { } // Create monitored element info. + const subject = new Subject(); const info: MonitoredElementInfo = { checkChildren: checkChildren, - subject: new Subject(), - rootNode + subject, + rootNode, + // Note that we want the observable to emit inside the NgZone, however we don't want to + // trigger change detection if nobody has subscribed to it. We do so by creating the + // observable manually. + observable: new Observable((observer: Observer) => { + const subscription = subject.subscribe(origin => { + this._ngZone.run(() => observer.next(origin)); + }); + + return () => { + subscription.unsubscribe(); + }; + }) }; this._elementInfo.set(nativeElement, info); this._registerGlobalListeners(info); - return info.subject.asObservable(); + return info.observable; } /** @@ -433,7 +447,7 @@ export class FocusMonitor implements OnDestroy { const origin = this._getFocusOrigin(event); this._setClasses(element, origin); - this._emitOrigin(elementInfo.subject, origin); + elementInfo.subject.next(origin); this._lastFocusOrigin = origin; } @@ -453,11 +467,7 @@ export class FocusMonitor implements OnDestroy { } this._setClasses(element); - this._emitOrigin(elementInfo.subject, null); - } - - private _emitOrigin(subject: Subject, origin: FocusOrigin) { - this._ngZone.run(() => subject.next(origin)); + elementInfo.subject.next(null); } private _registerGlobalListeners(elementInfo: MonitoredElementInfo) {