Skip to content

Commit 80aeaf3

Browse files
authored
fix(material-experimental/mdc-input): avoid double event listeners in ivy (#19052)
In Ivy the `host` metadata is inherited and merged which results in the MDC-based input binding its `focus`, `blur` and `input` events twice.
1 parent 7e66037 commit 80aeaf3

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed

src/material-experimental/mdc-input/input.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ import {MatInput as BaseMatInput} from '@angular/material/input';
3636
'[attr.aria-describedby]': '_ariaDescribedby || null',
3737
'[attr.aria-invalid]': 'errorState',
3838
'[attr.aria-required]': 'required.toString()',
39-
'(blur)': '_focusChanged(false)',
40-
'(focus)': '_focusChanged(true)',
41-
'(input)': '_onInput()',
4239
},
4340
providers: [{provide: MatFormFieldControl, useExisting: MatInput}],
4441
})

src/material/input/input.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
OnInit,
2222
Optional,
2323
Self,
24+
HostListener,
2425
} from '@angular/core';
2526
import {FormGroupDirective, NgControl, NgForm} from '@angular/forms';
2627
import {
@@ -83,9 +84,6 @@ const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof MatInputBase =
8384
'[attr.aria-describedby]': '_ariaDescribedby || null',
8485
'[attr.aria-invalid]': 'errorState',
8586
'[attr.aria-required]': 'required.toString()',
86-
'(blur)': '_focusChanged(false)',
87-
'(focus)': '_focusChanged(true)',
88-
'(input)': '_onInput()',
8987
},
9088
providers: [{provide: MatFormFieldControl, useExisting: MatInput}],
9189
})
@@ -314,14 +312,28 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
314312
this._elementRef.nativeElement.focus(options);
315313
}
316314

315+
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
316+
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
317+
// ViewEngine they're overwritten.
318+
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
317319
/** Callback for the cases where the focused state of the input changes. */
320+
// tslint:disable:no-host-decorator-in-concrete
321+
@HostListener('focus', ['true'])
322+
@HostListener('blur', ['false'])
323+
// tslint:enable:no-host-decorator-in-concrete
318324
_focusChanged(isFocused: boolean) {
319325
if (isFocused !== this.focused && (!this.readonly || !isFocused)) {
320326
this.focused = isFocused;
321327
this.stateChanges.next();
322328
}
323329
}
324330

331+
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
332+
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
333+
// ViewEngine they're overwritten.
334+
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
335+
// tslint:disable-next-line:no-host-decorator-in-concrete
336+
@HostListener('input')
325337
_onInput() {
326338
// This is a noop function and is used to let Angular know whenever the value changes.
327339
// Angular will run a new change detection each time the `input` event has been dispatched.

0 commit comments

Comments
 (0)