Skip to content

Commit dc4c51f

Browse files
committed
fix(focus-monitor): reenter ngzone before emitting
1 parent 0e37d6c commit dc4c51f

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
dispatchMouseEvent,
66
patchElementFocus,
77
} from '@angular/cdk/testing';
8-
import {Component} from '@angular/core';
8+
import {Component, ElementRef, ViewChild} from '@angular/core';
99
import {ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing';
1010
import {By} from '@angular/platform-browser';
1111
import {FocusMonitor, FocusOrigin, TOUCH_BUFFER_MS} from './focus-monitor';
@@ -212,7 +212,6 @@ describe('FocusMonitor', () => {
212212

213213
expect(buttonElement.classList.length).toBe(0, 'button should not have any focus classes');
214214
}));
215-
216215
});
217216

218217

@@ -393,6 +392,39 @@ describe('cdkMonitorFocus', () => {
393392
});
394393
});
395394

395+
describe('FocusMonitor observable stream', () => {
396+
let fixture: ComponentFixture<MonitoredElementRequiringChangeDetection>;
397+
let buttonElement: HTMLElement;
398+
let focusMonitor: FocusMonitor;
399+
400+
beforeEach(() => {
401+
TestBed.configureTestingModule({
402+
imports: [A11yModule],
403+
declarations: [
404+
MonitoredElementRequiringChangeDetection,
405+
],
406+
}).compileComponents();
407+
});
408+
409+
beforeEach(inject([FocusMonitor], (fm: FocusMonitor) => {
410+
fixture = TestBed.createComponent(MonitoredElementRequiringChangeDetection);
411+
fixture.detectChanges();
412+
413+
buttonElement = fixture.componentInstance.button.nativeElement;
414+
focusMonitor = fm;
415+
416+
patchElementFocus(buttonElement);
417+
}));
418+
419+
it('should emit inside the NgZone', () => {
420+
fixture.detectChanges();
421+
expect(buttonElement.innerText).toBe('');
422+
buttonElement.focus();
423+
fixture.detectChanges();
424+
expect(buttonElement.innerText).toBe('program');
425+
});
426+
});
427+
396428

397429
@Component({
398430
template: `<button>focus me!</button>`
@@ -418,3 +450,21 @@ class ComplexComponentWithMonitorElementFocus {}
418450
template: `<div tabindex="0" cdkMonitorSubtreeFocus><button></button></div>`
419451
})
420452
class ComplexComponentWithMonitorSubtreeFocus {}
453+
454+
@Component({
455+
template: `<button #b>{{origin}}</button>`
456+
})
457+
class MonitoredElementRequiringChangeDetection {
458+
@ViewChild('b') button: ElementRef;
459+
origin: string;
460+
461+
constructor(private _focusMonitor: FocusMonitor) {}
462+
463+
ngOnInit() {
464+
this._focusMonitor.monitor(this.button.nativeElement).subscribe(o => this.origin = o || '');
465+
}
466+
467+
ngOnDestroy() {
468+
this._focusMonitor.stopMonitoring(this.button.nativeElement);
469+
}
470+
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ export class FocusMonitor implements OnDestroy {
307307
}
308308

309309
this._setClasses(element, this._origin);
310-
elementInfo.subject.next(this._origin);
310+
this._emitOrigin(elementInfo.subject, this._origin);
311311
this._lastFocusOrigin = this._origin;
312312
this._origin = null;
313313
}
@@ -328,7 +328,11 @@ export class FocusMonitor implements OnDestroy {
328328
}
329329

330330
this._setClasses(element);
331-
elementInfo.subject.next(null);
331+
this._emitOrigin(elementInfo.subject, null);
332+
}
333+
334+
private _emitOrigin(subject: Subject<FocusOrigin>, origin: FocusOrigin) {
335+
this._ngZone.run(() => subject.next(origin));
332336
}
333337

334338
private _incrementMonitoredElementCount() {
@@ -345,7 +349,6 @@ export class FocusMonitor implements OnDestroy {
345349
this._unregisterGlobalListeners = () => {};
346350
}
347351
}
348-
349352
}
350353

351354

0 commit comments

Comments
 (0)