diff --git a/src/demo-app/datepicker/datepicker-demo.html b/src/demo-app/datepicker/datepicker-demo.html
index 03de75ca83d..b9c6c7dba22 100644
--- a/src/demo-app/datepicker/datepicker-demo.html
+++ b/src/demo-app/datepicker/datepicker-demo.html
@@ -121,3 +121,15 @@
[startView]="yearView ? 'year' : 'month'">
+
+Datepicker with value property binding
+
+
+
+
+
+
+
diff --git a/src/lib/datepicker/datepicker-input.ts b/src/lib/datepicker/datepicker-input.ts
index 45b847adc84..4b06adaf958 100644
--- a/src/lib/datepicker/datepicker-input.ts
+++ b/src/lib/datepicker/datepicker-input.ts
@@ -19,7 +19,7 @@ import {
OnDestroy,
Optional,
Output,
- Renderer2,
+ Renderer2
} from '@angular/core';
import {
AbstractControl,
@@ -29,10 +29,11 @@ import {
ValidationErrors,
Validator,
ValidatorFn,
- Validators,
+ Validators
} from '@angular/forms';
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
import {MatFormField} from '@angular/material/form-field';
+import {MAT_INPUT_VALUE_ACCESSOR} from '@angular/material/input';
import {Subscription} from 'rxjs/Subscription';
import {MatDatepicker} from './datepicker';
import {createMissingDateImplError} from './datepicker-errors';
@@ -70,7 +71,11 @@ export class MatDatepickerInputEvent {
/** Directive used to connect an input to a MatDatepicker. */
@Directive({
selector: 'input[matDatepicker]',
- providers: [MAT_DATEPICKER_VALUE_ACCESSOR, MAT_DATEPICKER_VALIDATORS],
+ providers: [
+ MAT_DATEPICKER_VALUE_ACCESSOR,
+ MAT_DATEPICKER_VALIDATORS,
+ {provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatDatepickerInput},
+ ],
host: {
'[attr.aria-haspopup]': 'true',
'[attr.aria-owns]': '(_datepicker?.opened && _datepicker.id) || null',
diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts
index 98fc0461215..81576b71727 100644
--- a/src/lib/datepicker/datepicker.spec.ts
+++ b/src/lib/datepicker/datepicker.spec.ts
@@ -30,6 +30,8 @@ import {MatDatepickerIntl, MatDatepickerModule} from './index';
describe('MatDatepicker', () => {
+ const SUPPORTS_INTL = typeof Intl != 'undefined';
+
afterEach(inject([OverlayContainer], (container: OverlayContainer) => {
container.getContainerElement().parentNode!.removeChild(container.getContainerElement());
}));
@@ -83,6 +85,12 @@ describe('MatDatepicker', () => {
fixture.detectChanges();
}));
+ it('should initialize with correct value shown in input', () => {
+ if (SUPPORTS_INTL) {
+ expect(fixture.nativeElement.querySelector('input').value).toBe('1/1/2020');
+ }
+ });
+
it('open non-touch should open popup', () => {
expect(document.querySelector('.cdk-overlay-pane.mat-datepicker-popup')).toBeNull();
diff --git a/src/lib/input/input-value-accessor.ts b/src/lib/input/input-value-accessor.ts
new file mode 100644
index 00000000000..158c8b7bba1
--- /dev/null
+++ b/src/lib/input/input-value-accessor.ts
@@ -0,0 +1,19 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {InjectionToken} from '@angular/core';
+
+
+/**
+ * This token is used to inject the object whose value should be set into `MatInput`. If none is
+ * provided, the native `HTMLInputElement` is used. Directives like `MatDatepickerInput` can provide
+ * themselves for this token, in order to make `MatInput` delegate the getting and setting of the
+ * value to them.
+ */
+export const MAT_INPUT_VALUE_ACCESSOR =
+ new InjectionToken<{value: any}>('MAT_INPUT_VALUE_ACCESSOR');
diff --git a/src/lib/input/input.ts b/src/lib/input/input.ts
index 204f6b8a855..a1171211ad9 100644
--- a/src/lib/input/input.ts
+++ b/src/lib/input/input.ts
@@ -6,24 +6,26 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {getSupportedInputTypes, Platform} from '@angular/cdk/platform';
import {
Directive,
DoCheck,
ElementRef,
+ Inject,
Input,
OnChanges,
OnDestroy,
Optional,
Renderer2,
- Self,
+ Self
} from '@angular/core';
-import {coerceBooleanProperty} from '@angular/cdk/coercion';
-import {FormGroupDirective, NgControl, NgForm, FormControl} from '@angular/forms';
-import {Platform, getSupportedInputTypes} from '@angular/cdk/platform';
-import {getMatInputUnsupportedTypeError} from './input-errors';
+import {FormControl, FormGroupDirective, NgControl, NgForm} from '@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
-import {Subject} from 'rxjs/Subject';
import {MatFormFieldControl} from '@angular/material/form-field';
+import {Subject} from 'rxjs/Subject';
+import {getMatInputUnsupportedTypeError} from './input-errors';
+import {MAT_INPUT_VALUE_ACCESSOR} from './input-value-accessor';
// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
const MAT_INPUT_INVALID_TYPES = [
@@ -70,7 +72,7 @@ export class MatInput implements MatFormFieldControl, OnChanges, OnDestroy,
protected _required = false;
protected _id: string;
protected _uid = `mat-input-${nextUniqueId++}`;
- protected _previousNativeValue = this.value;
+ protected _previousNativeValue: any;
private _readonly = false;
/** Whether the input is focused. */
@@ -129,10 +131,10 @@ export class MatInput implements MatFormFieldControl, OnChanges, OnDestroy,
/** The input element's value. */
@Input()
- get value() { return this._elementRef.nativeElement.value; }
- set value(value: string) {
+ get value(): any { return this._inputValueAccessor.value; }
+ set value(value: any) {
if (value !== this.value) {
- this._elementRef.nativeElement.value = value;
+ this._inputValueAccessor.value = value;
this.stateChanges.next();
}
}
@@ -157,7 +159,14 @@ export class MatInput implements MatFormFieldControl, OnChanges, OnDestroy,
@Optional() @Self() public ngControl: NgControl,
@Optional() protected _parentForm: NgForm,
@Optional() protected _parentFormGroup: FormGroupDirective,
- private _defaultErrorStateMatcher: ErrorStateMatcher) {
+ private _defaultErrorStateMatcher: ErrorStateMatcher,
+ @Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR)
+ private _inputValueAccessor: {value: any}) {
+ // If no input value accessor was explicitly specified, use the element as the input value
+ // accessor.
+ this._inputValueAccessor = this._inputValueAccessor || this._elementRef.nativeElement;
+
+ this._previousNativeValue = this.value;
// Force setter to be called in case id was not specified.
this.id = this.id;
diff --git a/src/lib/input/public-api.ts b/src/lib/input/public-api.ts
index bc0416c7941..5eaaf7d5057 100644
--- a/src/lib/input/public-api.ts
+++ b/src/lib/input/public-api.ts
@@ -11,5 +11,5 @@ export * from './input-module';
export * from './autosize';
export * from './input';
export * from './input-errors';
-
+export * from './input-value-accessor';