Skip to content

Commit

Permalink
fix(material-experimental/mdc-input): avoid double event listeners in…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
crisbeto authored Apr 20, 2020
1 parent 7e66037 commit 80aeaf3
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 6 deletions.
3 changes: 0 additions & 3 deletions src/material-experimental/mdc-input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ import {MatInput as BaseMatInput} from '@angular/material/input';
'[attr.aria-describedby]': '_ariaDescribedby || null',
'[attr.aria-invalid]': 'errorState',
'[attr.aria-required]': 'required.toString()',
'(blur)': '_focusChanged(false)',
'(focus)': '_focusChanged(true)',
'(input)': '_onInput()',
},
providers: [{provide: MatFormFieldControl, useExisting: MatInput}],
})
Expand Down
18 changes: 15 additions & 3 deletions src/material/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
OnInit,
Optional,
Self,
HostListener,
} from '@angular/core';
import {FormGroupDirective, NgControl, NgForm} from '@angular/forms';
import {
Expand Down Expand Up @@ -83,9 +84,6 @@ const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof MatInputBase =
'[attr.aria-describedby]': '_ariaDescribedby || null',
'[attr.aria-invalid]': 'errorState',
'[attr.aria-required]': 'required.toString()',
'(blur)': '_focusChanged(false)',
'(focus)': '_focusChanged(true)',
'(input)': '_onInput()',
},
providers: [{provide: MatFormFieldControl, useExisting: MatInput}],
})
Expand Down Expand Up @@ -314,14 +312,28 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
this._elementRef.nativeElement.focus(options);
}

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

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

0 comments on commit 80aeaf3

Please sign in to comment.