diff --git a/src/dev-app/input/BUILD.bazel b/src/dev-app/input/BUILD.bazel
index 56ea279eb053..387aae9b04c7 100644
--- a/src/dev-app/input/BUILD.bazel
+++ b/src/dev-app/input/BUILD.bazel
@@ -21,6 +21,7 @@ ng_module(
"//src/material/input",
"//src/material/tabs",
"//src/material/toolbar",
+ "//src/material/tooltip",
"@npm//@angular/forms",
],
)
diff --git a/src/dev-app/input/input-demo.html b/src/dev-app/input/input-demo.html
index 0742fa5c891e..d41d02055512 100644
--- a/src/dev-app/input/input-demo.html
+++ b/src/dev-app/input/input-demo.html
@@ -58,6 +58,19 @@
+
+ Number Input + Tooltip
+
+
+
+
+
Error messages
diff --git a/src/dev-app/input/input-demo.ts b/src/dev-app/input/input-demo.ts
index fbe2668b1d1a..36caf31c80e0 100644
--- a/src/dev-app/input/input-demo.ts
+++ b/src/dev-app/input/input-demo.ts
@@ -28,6 +28,7 @@ import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatIconModule} from '@angular/material/icon';
import {MatTabsModule} from '@angular/material/tabs';
import {MatToolbarModule} from '@angular/material/toolbar';
+import {MatTooltipModule} from '@angular/material/tooltip';
let max = 5;
@@ -55,6 +56,7 @@ const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA
FormFieldCustomControlExample,
MyTelInput,
ReactiveFormsModule,
+ MatTooltipModule,
],
})
export class InputDemo {
diff --git a/src/material/input/input.ts b/src/material/input/input.ts
index b97ef537734f..ad0a96b58a3f 100644
--- a/src/material/input/input.ts
+++ b/src/material/input/input.ts
@@ -87,6 +87,7 @@ export class MatInput
private _inputValueAccessor: {value: any};
private _previousPlaceholder: string | null;
private _errorStateTracker: _ErrorStateTracker;
+ private _webkitBlinkWheelListenerAttached = false;
/** Whether the component is being rendered on the server. */
readonly _isServer: boolean;
@@ -197,6 +198,8 @@ export class MatInput
if (!this._isTextarea && getSupportedInputTypes().has(this._type)) {
(this._elementRef.nativeElement as HTMLInputElement).type = this._type;
}
+
+ this._ensureWheelDefaultBehavior();
}
protected _type = 'text';
@@ -266,7 +269,7 @@ export class MatInput
defaultErrorStateMatcher: ErrorStateMatcher,
@Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR) inputValueAccessor: any,
private _autofillMonitor: AutofillMonitor,
- ngZone: NgZone,
+ private _ngZone: NgZone,
// TODO: Remove this once the legacy appearance has been removed. We only need
// to inject the form field for determining whether the placeholder has been promoted.
@Optional() @Inject(MAT_FORM_FIELD) protected _formField?: MatFormField,
@@ -287,7 +290,7 @@ export class MatInput
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
// exists on iOS, we only bother to install the listener on iOS.
if (_platform.IOS) {
- ngZone.runOutsideAngular(() => {
+ _ngZone.runOutsideAngular(() => {
_elementRef.nativeElement.addEventListener('keyup', this._iOSKeyupListener);
});
}
@@ -334,6 +337,10 @@ export class MatInput
if (this._platform.IOS) {
this._elementRef.nativeElement.removeEventListener('keyup', this._iOSKeyupListener);
}
+
+ if (this._webkitBlinkWheelListenerAttached) {
+ this._elementRef.nativeElement.removeEventListener('wheel', this._webkitBlinkWheelListener);
+ }
}
ngDoCheck() {
@@ -527,4 +534,36 @@ export class MatInput
el.setSelectionRange(0, 0);
}
};
+
+ private _webkitBlinkWheelListener = (): void => {
+ // This is a noop function and is used to enable mouse wheel input
+ // on number inputs
+ // on blink and webkit browsers.
+ };
+
+ /**
+ * In blink and webkit browsers a focused number input does not increment or decrement its value
+ * on mouse wheel interaction unless a wheel event listener is attached to it or one of its ancestors or a passive wheel listener is attached somewhere in the DOM.
+ * For example: Hitting a tooltip once enables the mouse wheel input for all number inputs as long as it exists.
+ * In order to get reliable and intuitive behavior we apply a wheel event on our own
+ * thus making sure increment and decrement by mouse wheel works every time.
+ * @docs-private
+ */
+ private _ensureWheelDefaultBehavior(): void {
+ if (
+ !this._webkitBlinkWheelListenerAttached &&
+ this._type === 'number' &&
+ (this._platform.BLINK || this._platform.WEBKIT)
+ ) {
+ this._ngZone.runOutsideAngular(() => {
+ this._elementRef.nativeElement.addEventListener('wheel', this._webkitBlinkWheelListener);
+ });
+ this._webkitBlinkWheelListenerAttached = true;
+ }
+
+ if (this._webkitBlinkWheelListenerAttached && this._type !== 'number') {
+ this._elementRef.nativeElement.removeEventListener('wheel', this._webkitBlinkWheelListener);
+ this._webkitBlinkWheelListenerAttached = true;
+ }
+ }
}
diff --git a/tools/public_api_guard/material/input.md b/tools/public_api_guard/material/input.md
index 3f7c5abab3bf..36ff273b47a2 100644
--- a/tools/public_api_guard/material/input.md
+++ b/tools/public_api_guard/material/input.md
@@ -47,7 +47,7 @@ export { MatHint }
// @public (undocumented)
export class MatInput implements MatFormFieldControl, OnChanges, OnDestroy, AfterViewInit, DoCheck {
- constructor(_elementRef: ElementRef, _platform: Platform, ngControl: NgControl, parentForm: NgForm, parentFormGroup: FormGroupDirective, defaultErrorStateMatcher: ErrorStateMatcher, inputValueAccessor: any, _autofillMonitor: AutofillMonitor, ngZone: NgZone, _formField?: MatFormField | undefined);
+ constructor(_elementRef: ElementRef, _platform: Platform, ngControl: NgControl, parentForm: NgForm, parentFormGroup: FormGroupDirective, defaultErrorStateMatcher: ErrorStateMatcher, inputValueAccessor: any, _autofillMonitor: AutofillMonitor, _ngZone: NgZone, _formField?: MatFormField | undefined);
autofilled: boolean;
controlType: string;
protected _dirtyCheckNativeValue(): void;