mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(datepicker): toggle not reacting to disabled state changes in datepicker or input (#6964)
Fixes the datepicker toggle not reacting to changes in the disabled state of the associated datepicker and datepicker input.
This commit is contained in:
committed by
mmalerba
parent
9fe6386eec
commit
85993d3a96
@@ -4,22 +4,26 @@
|
||||
<md-checkbox [(ngModel)]="filterOdd">Filter odd months and dates</md-checkbox>
|
||||
<md-checkbox [(ngModel)]="yearView">Start in year view</md-checkbox>
|
||||
<md-checkbox [(ngModel)]="datepickerDisabled">Disable datepicker</md-checkbox>
|
||||
<md-checkbox [(ngModel)]="inputDisabled">Disable input</md-checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<md-form-field>
|
||||
<input mdInput [mdDatepicker]="minDatePicker" [(ngModel)]="minDate" placeholder="Min date">
|
||||
<input mdInput [mdDatepicker]="minDatePicker" [(ngModel)]="minDate" placeholder="Min date"
|
||||
[disabled]="inputDisabled">
|
||||
<md-datepicker-toggle mdSuffix [for]="minDatePicker"></md-datepicker-toggle>
|
||||
<md-datepicker #minDatePicker [touchUi]="touch" [disabled]="datepickerDisabled"></md-datepicker>
|
||||
</md-form-field>
|
||||
<md-form-field>
|
||||
<input mdInput [mdDatepicker]="maxDatePicker" [(ngModel)]="maxDate" placeholder="Max date">
|
||||
<input mdInput [mdDatepicker]="maxDatePicker" [(ngModel)]="maxDate" placeholder="Max date"
|
||||
[disabled]="inputDisabled">
|
||||
<md-datepicker-toggle mdSuffix [for]="maxDatePicker"></md-datepicker-toggle>
|
||||
<md-datepicker #maxDatePicker [touchUi]="touch" [disabled]="datepickerDisabled"></md-datepicker>
|
||||
</md-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<md-form-field>
|
||||
<input mdInput [mdDatepicker]="startAtPicker" [(ngModel)]="startAt" placeholder="Start at date">
|
||||
<input mdInput [mdDatepicker]="startAtPicker" [(ngModel)]="startAt" placeholder="Start at date"
|
||||
[disabled]="inputDisabled">
|
||||
<md-datepicker-toggle mdSuffix [for]="startAtPicker"></md-datepicker-toggle>
|
||||
<md-datepicker #startAtPicker [touchUi]="touch" [disabled]="datepickerDisabled"></md-datepicker>
|
||||
</md-form-field>
|
||||
@@ -37,6 +41,7 @@
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[mdDatepickerFilter]="filterOdd ? dateFilter : null"
|
||||
[disabled]="inputDisabled"
|
||||
placeholder="Pick a date"
|
||||
(dateInput)="onDateInput($event)"
|
||||
(dateChange)="onDateChange($event)">
|
||||
@@ -64,6 +69,7 @@
|
||||
[(ngModel)]="date"
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[disabled]="inputDisabled"
|
||||
[mdDatepickerFilter]="filterOdd ? dateFilter : null"
|
||||
placeholder="Pick a date">
|
||||
<md-datepicker-toggle [for]="resultPicker2"></md-datepicker-toggle>
|
||||
@@ -81,7 +87,7 @@
|
||||
<md-datepicker-toggle [for]="datePicker1"></md-datepicker-toggle>
|
||||
<md-form-field>
|
||||
<input mdInput [mdDatepicker]="datePicker1" [(ngModel)]="date" [min]="minDate" [max]="maxDate"
|
||||
[mdDatepickerFilter]="filterOdd ? dateFilter : null" [disabled]="true"
|
||||
[mdDatepickerFilter]="filterOdd ? dateFilter : null" disabled
|
||||
placeholder="Input disabled">
|
||||
<md-datepicker #datePicker1 [touchUi]="touch" [startAt]="startAt"
|
||||
[startView]="yearView ? 'year' : 'month'"></md-datepicker>
|
||||
|
||||
@@ -153,7 +153,12 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
|
||||
@Input()
|
||||
get disabled() { return this._disabled; }
|
||||
set disabled(value: any) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
const newValue = coerceBooleanProperty(value);
|
||||
|
||||
if (this._disabled !== newValue) {
|
||||
this._disabled = newValue;
|
||||
this._disabledChange.emit(newValue);
|
||||
}
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
@@ -166,6 +171,9 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
|
||||
/** Emits when the value changes (either due to user input or programmatic change). */
|
||||
_valueChange = new EventEmitter<D|null>();
|
||||
|
||||
/** Emits when the disabled state has changed */
|
||||
_disabledChange = new EventEmitter<boolean>();
|
||||
|
||||
_onTouched = () => {};
|
||||
|
||||
private _cvaOnChange: (value: any) => void = () => {};
|
||||
@@ -237,6 +245,8 @@ export class MdDatepickerInput<D> implements AfterContentInit, ControlValueAcces
|
||||
|
||||
ngOnDestroy() {
|
||||
this._datepickerSubscription.unsubscribe();
|
||||
this._valueChange.complete();
|
||||
this._disabledChange.complete();
|
||||
}
|
||||
|
||||
registerOnValidatorChange(fn: () => void): void {
|
||||
|
||||
@@ -12,12 +12,16 @@ import {
|
||||
Input,
|
||||
ViewEncapsulation,
|
||||
OnDestroy,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import {MdDatepicker} from './datepicker';
|
||||
import {MdDatepickerIntl} from './datepicker-intl';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {of as observableOf} from 'rxjs/observable/of';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -30,8 +34,8 @@ import {Subscription} from 'rxjs/Subscription';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MdDatepickerToggle<D> implements OnDestroy {
|
||||
private _intlChanges: Subscription;
|
||||
export class MdDatepickerToggle<D> implements OnChanges, OnDestroy {
|
||||
private _stateChanges = Subscription.EMPTY;
|
||||
|
||||
/** Datepicker instance that the button will toggle. */
|
||||
@Input('for') datepicker: MdDatepicker<D>;
|
||||
@@ -46,12 +50,26 @@ export class MdDatepickerToggle<D> implements OnDestroy {
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
constructor(public _intl: MdDatepickerIntl, changeDetectorRef: ChangeDetectorRef) {
|
||||
this._intlChanges = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
|
||||
constructor(
|
||||
public _intl: MdDatepickerIntl,
|
||||
private _changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.datepicker) {
|
||||
const datepicker: MdDatepicker<D> = changes.datepicker.currentValue;
|
||||
const datepickerDisabled = datepicker ? datepicker._disabledChange : observableOf();
|
||||
const inputDisabled = datepicker && datepicker._datepickerInput ?
|
||||
datepicker._datepickerInput._disabledChange :
|
||||
observableOf();
|
||||
|
||||
this._stateChanges.unsubscribe();
|
||||
this._stateChanges = merge(this._intl.changes, datepickerDisabled, inputDisabled)
|
||||
.subscribe(() => this._changeDetectorRef.markForCheck());
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._intlChanges.unsubscribe();
|
||||
this._stateChanges.unsubscribe();
|
||||
}
|
||||
|
||||
_open(event: Event): void {
|
||||
|
||||
@@ -552,11 +552,28 @@ describe('MdDatepicker', () => {
|
||||
it('should not open calendar when toggle clicked if datepicker is disabled', () => {
|
||||
testComponent.datepicker.disabled = true;
|
||||
fixture.detectChanges();
|
||||
const toggle = fixture.debugElement.query(By.css('button')).nativeElement;
|
||||
|
||||
expect(toggle.hasAttribute('disabled')).toBe(true);
|
||||
expect(document.querySelector('md-dialog-container')).toBeNull();
|
||||
|
||||
let toggle = fixture.debugElement.query(By.css('button'));
|
||||
dispatchMouseEvent(toggle.nativeElement, 'click');
|
||||
dispatchMouseEvent(toggle, 'click');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(document.querySelector('md-dialog-container')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not open calendar when toggle clicked if input is disabled', () => {
|
||||
expect(testComponent.datepicker.disabled).toBeUndefined();
|
||||
|
||||
testComponent.input.disabled = true;
|
||||
fixture.detectChanges();
|
||||
const toggle = fixture.debugElement.query(By.css('button')).nativeElement;
|
||||
|
||||
expect(toggle.hasAttribute('disabled')).toBe(true);
|
||||
expect(document.querySelector('md-dialog-container')).toBeNull();
|
||||
|
||||
dispatchMouseEvent(toggle, 'click');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(document.querySelector('md-dialog-container')).toBeNull();
|
||||
@@ -1075,6 +1092,7 @@ class DatepickerWithFormControl {
|
||||
})
|
||||
class DatepickerWithToggle {
|
||||
@ViewChild('d') datepicker: MdDatepicker<Date>;
|
||||
@ViewChild(MdDatepickerInput) input: MdDatepickerInput<Date>;
|
||||
touchUI = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import {MdDialog} from '../dialog/dialog';
|
||||
import {MdDialogRef} from '../dialog/dialog-ref';
|
||||
import {MdDatepickerInput} from './datepicker-input';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {DateAdapter} from '../core/datetime/index';
|
||||
import {createMissingDateImplError} from './datepicker-errors';
|
||||
import {MdCalendar} from './calendar';
|
||||
@@ -150,7 +151,12 @@ export class MdDatepicker<D> implements OnDestroy {
|
||||
return this._disabled === undefined ? this._datepickerInput.disabled : this._disabled;
|
||||
}
|
||||
set disabled(value: any) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
const newValue = coerceBooleanProperty(value);
|
||||
|
||||
if (newValue !== this._disabled) {
|
||||
this._disabled = newValue;
|
||||
this._disabledChange.next(newValue);
|
||||
}
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
@@ -194,14 +200,17 @@ export class MdDatepicker<D> implements OnDestroy {
|
||||
/** A portal containing the calendar for this datepicker. */
|
||||
private _calendarPortal: ComponentPortal<MdDatepickerContent<D>>;
|
||||
|
||||
/** The input element this datepicker is associated with. */
|
||||
private _datepickerInput: MdDatepickerInput<D>;
|
||||
|
||||
/** The element that was focused before the datepicker was opened. */
|
||||
private _focusedElementBeforeOpen: HTMLElement | null = null;
|
||||
|
||||
private _inputSubscription = Subscription.EMPTY;
|
||||
|
||||
/** The input element this datepicker is associated with. */
|
||||
_datepickerInput: MdDatepickerInput<D>;
|
||||
|
||||
/** Emits when the datepicker is disabled. */
|
||||
_disabledChange = new Subject<boolean>();
|
||||
|
||||
constructor(private _dialog: MdDialog,
|
||||
private _overlay: Overlay,
|
||||
private _ngZone: NgZone,
|
||||
@@ -218,6 +227,7 @@ export class MdDatepicker<D> implements OnDestroy {
|
||||
ngOnDestroy() {
|
||||
this.close();
|
||||
this._inputSubscription.unsubscribe();
|
||||
this._disabledChange.complete();
|
||||
|
||||
if (this._popupRef) {
|
||||
this._popupRef.dispose();
|
||||
|
||||
Reference in New Issue
Block a user