mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(datepicker): prevent matInput from clobbering date value (#7831)
This commit is contained in:
@@ -121,3 +121,15 @@
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
<h2>Datepicker with value property binding</h2>
|
||||
<p>
|
||||
<mat-datepicker-toggle [for]="datePicker4"></mat-datepicker-toggle>
|
||||
<mat-form-field>
|
||||
<input matInput [matDatepicker]="datePicker4" [value]="date" [min]="minDate"
|
||||
[max]="maxDate" [matDatepickerFilter]="filterOdd ? dateFilter : null"
|
||||
placeholder="Value binding">
|
||||
<mat-datepicker #datePicker4 [touchUi]="touch" [startAt]="startAt"
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
@@ -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<D> {
|
||||
/** 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',
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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');
|
||||
+20
-11
@@ -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<any>, 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<any>, 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<any>, 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;
|
||||
|
||||
@@ -11,5 +11,5 @@ export * from './input-module';
|
||||
export * from './autosize';
|
||||
export * from './input';
|
||||
export * from './input-errors';
|
||||
|
||||
export * from './input-value-accessor';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user