Skip to content

Commit c38ae56

Browse files
committed
fix(focus-monitor): reenter ngzone before emitting
1 parent fbf06bb commit c38ae56

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
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, flush, inject, TestBed, tick} from '@angular/core/testing';
1010
import {By} from '@angular/platform-browser';
1111
import {A11yModule} from '../index';
@@ -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

@@ -424,6 +423,39 @@ describe('cdkMonitorFocus', () => {
424423
});
425424
});
426425

426+
describe('FocusMonitor observable stream', () => {
427+
let fixture: ComponentFixture<MonitoredElementRequiringChangeDetection>;
428+
let buttonElement: HTMLElement;
429+
let focusMonitor: FocusMonitor;
430+
431+
beforeEach(() => {
432+
TestBed.configureTestingModule({
433+
imports: [A11yModule],
434+
declarations: [
435+
MonitoredElementRequiringChangeDetection,
436+
],
437+
}).compileComponents();
438+
});
439+
440+
beforeEach(inject([FocusMonitor], (fm: FocusMonitor) => {
441+
fixture = TestBed.createComponent(MonitoredElementRequiringChangeDetection);
442+
fixture.detectChanges();
443+
444+
buttonElement = fixture.componentInstance.button.nativeElement;
445+
focusMonitor = fm;
446+
447+
patchElementFocus(buttonElement);
448+
}));
449+
450+
it('should emit inside the NgZone', () => {
451+
fixture.detectChanges();
452+
expect(buttonElement.innerText).toBe('');
453+
buttonElement.focus();
454+
fixture.detectChanges();
455+
expect(buttonElement.innerText).toBe('program');
456+
});
457+
});
458+
427459

428460
@Component({
429461
template: `<button>focus me!</button>`
@@ -454,3 +486,21 @@ class ComplexComponentWithMonitorSubtreeFocus {}
454486
template: `<div cdkMonitorSubtreeFocus><button cdkMonitorElementFocus></button></div>`
455487
})
456488
class ComplexComponentWithMonitorSubtreeFocusAnfMonitorElementFocus {}
489+
490+
@Component({
491+
template: `<button #b>{{origin}}</button>`
492+
})
493+
class MonitoredElementRequiringChangeDetection {
494+
@ViewChild('b') button: ElementRef;
495+
origin: string;
496+
497+
constructor(private _focusMonitor: FocusMonitor) {}
498+
499+
ngOnInit() {
500+
this._focusMonitor.monitor(this.button.nativeElement).subscribe(o => this.origin = o || '');
501+
}
502+
503+
ngOnDestroy() {
504+
this._focusMonitor.stopMonitoring(this.button.nativeElement);
505+
}
506+
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ export class FocusMonitor implements OnDestroy {
314314
}
315315

316316
this._setClasses(element, origin);
317-
elementInfo.subject.next(origin);
317+
this._emitOrigin(elementInfo.subject, origin);
318318
this._lastFocusOrigin = origin;
319319
}
320320

@@ -334,7 +334,11 @@ export class FocusMonitor implements OnDestroy {
334334
}
335335

336336
this._setClasses(element);
337-
elementInfo.subject.next(null);
337+
this._emitOrigin(elementInfo.subject, null);
338+
}
339+
340+
private _emitOrigin(subject: Subject<FocusOrigin>, origin: FocusOrigin) {
341+
this._ngZone.run(() => subject.next(origin));
338342
}
339343

340344
private _incrementMonitoredElementCount() {

0 commit comments

Comments
 (0)