Skip to content

Commit b247539

Browse files
committed
address comments
1 parent a063401 commit b247539

File tree

3 files changed

+40
-9
lines changed

3 files changed

+40
-9
lines changed

src/lib/input/_autofill.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Core styles that enable monitoring autofill state of inputs.
22
@mixin mat-input-autofill {
3-
// Keyframes that do nothing, but allow us to monitor when an input becomes autofilled.
3+
// Keyframes that apply no styles, but allow us to monitor when an input becomes autofilled
4+
// by watching for the animation events that are fired when they start.
5+
// Based on: https://medium.com/@brunn/detecting-autofilled-fields-in-javascript-aed598d25da7
46
@keyframes mat-input-autofill-start {}
57
@keyframes mat-input-autofill-end {}
68

src/lib/input/autofill.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {supportsPassiveEventListeners} from '@angular/cdk/platform';
910
import {Component, ElementRef, ViewChild} from '@angular/core';
1011
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
12+
import {empty as observableEmpty} from 'rxjs/observable/empty';
1113
import {AutofillEvent, AutofillMonitor} from './autofill';
1214
import {MatInputModule} from './input-module';
13-
import {empty as observableEmpty} from 'rxjs/observable/empty';
15+
16+
17+
const listenerOptions: any = supportsPassiveEventListeners() ? {passive: true} : false;
18+
1419

1520
describe('AutofillMonitor', () => {
1621
let autofillMonitor: AutofillMonitor;
@@ -48,7 +53,8 @@ describe('AutofillMonitor', () => {
4853

4954
autofillMonitor.monitor(inputEl);
5055
expect(inputEl.classList).toContain('mat-input-autofill-monitored');
51-
expect(inputEl.addEventListener).toHaveBeenCalledWith('animationstart', jasmine.any(Function));
56+
expect(inputEl.addEventListener)
57+
.toHaveBeenCalledWith('animationstart', jasmine.any(Function), listenerOptions);
5258
});
5359

5460
it('should not add multiple listeners to the same element', () => {
@@ -69,7 +75,7 @@ describe('AutofillMonitor', () => {
6975
autofillMonitor.stopMonitoring(inputEl);
7076
expect(inputEl.classList).not.toContain('mat-input-autofill-monitored');
7177
expect(inputEl.removeEventListener)
72-
.toHaveBeenCalledWith('animationstart', jasmine.any(Function));
78+
.toHaveBeenCalledWith('animationstart', jasmine.any(Function), listenerOptions);
7379
});
7480

7581
it('should stop monitoring all monitored elements upon destroy', () => {

src/lib/input/autofill.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Directive, ElementRef, EventEmitter, Injectable, OnDestroy, Output} from '@angular/core';
9+
import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform';
10+
import {
11+
Directive,
12+
ElementRef,
13+
EventEmitter,
14+
Injectable,
15+
NgZone,
16+
OnDestroy,
17+
Output
18+
} from '@angular/core';
1019
import {Observable} from 'rxjs/Observable';
20+
import {empty as observableEmpty} from 'rxjs/observable/empty';
1121
import {Subject} from 'rxjs/Subject';
1222

1323

@@ -25,6 +35,10 @@ type MonitoredElementInfo = {
2535
};
2636

2737

38+
/** Options to pass to the animationstart listener. */
39+
const listenerOptions: any = supportsPassiveEventListeners() ? {passive: true} : false;
40+
41+
2842
/**
2943
* An injectable service that can be used to monitor the autofill state of an input.
3044
* Based on the following blog post:
@@ -34,7 +48,13 @@ type MonitoredElementInfo = {
3448
export class AutofillMonitor implements OnDestroy {
3549
private _monitoredElements = new Map<Element, MonitoredElementInfo>();
3650

51+
constructor(private _platform: Platform, private _ngZone: NgZone) {}
52+
3753
monitor(element: Element): Observable<AutofillEvent> {
54+
if (!this._platform.isBrowser) {
55+
return observableEmpty();
56+
}
57+
3858
const info = this._monitoredElements.get(element);
3959
if (info) {
4060
return info.subject.asObservable();
@@ -51,13 +71,15 @@ export class AutofillMonitor implements OnDestroy {
5171
}
5272
};
5373

54-
element.addEventListener('animationstart', listener);
74+
this._ngZone.runOutsideAngular(() => {
75+
element.addEventListener('animationstart', listener, listenerOptions);
76+
});
5577
element.classList.add('mat-input-autofill-monitored');
5678

5779
this._monitoredElements.set(element, {
5880
subject: result,
5981
unlisten: () => {
60-
element.removeEventListener('animationstart', listener);
82+
element.removeEventListener('animationstart', listener, listenerOptions);
6183
}
6284
});
6385

@@ -90,9 +112,10 @@ export class AutofillMonitor implements OnDestroy {
90112
export class MatAutofill implements OnDestroy {
91113
@Output() matAutofill = new EventEmitter<AutofillEvent>();
92114

93-
constructor(private _elementRef: ElementRef, private _autofillMonitor: AutofillMonitor) {
115+
constructor(private _elementRef: ElementRef, private _autofillMonitor: AutofillMonitor,
116+
ngZone: NgZone) {
94117
this._autofillMonitor.monitor(this._elementRef.nativeElement)
95-
.subscribe(event => this.matAutofill.emit(event));
118+
.subscribe(event => ngZone.run(() => this.matAutofill.emit(event)));
96119
}
97120

98121
ngOnDestroy() {

0 commit comments

Comments
 (0)