diff --git a/src/lib/datepicker/datepicker-input.ts b/src/lib/datepicker/datepicker-input.ts index c78d9a6e73f3..f7be57566e25 100644 --- a/src/lib/datepicker/datepicker-input.ts +++ b/src/lib/datepicker/datepicker-input.ts @@ -1,8 +1,12 @@ -import {AfterContentInit, Directive, ElementRef, forwardRef, Input, Renderer} from '@angular/core'; +import { + AfterContentInit, Directive, ElementRef, forwardRef, Input, OnDestroy, + Renderer +} from '@angular/core'; import {MdDatepicker} from './datepicker'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {SimpleDate} from '../core/datetime/simple-date'; import {CalendarLocale} from '../core/datetime/calendar-locale'; +import {Subscription} from 'rxjs'; export const MD_DATEPICKER_VALUE_ACCESSOR: any = { @@ -21,7 +25,7 @@ export const MD_DATEPICKER_VALUE_ACCESSOR: any = { '(blur)': '_onTouched()', } }) -export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor { +export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor, OnDestroy { @Input() set mdDatepicker(value: MdDatepicker) { if (value) { @@ -31,6 +35,17 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor } private _datepicker: MdDatepicker; + @Input() + get value(): SimpleDate { + return this._value; + } + set value(value: SimpleDate) { + this._value = this._locale.parseDate(value); + const stringValue = this._value == null ? '' : this._locale.formatDate(this._value); + this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', stringValue); + } + private _value: SimpleDate; + @Input() set matDatepicker(value: MdDatepicker) { this.mdDatepicker = value; } @@ -38,32 +53,30 @@ export class MdDatepickerInput implements AfterContentInit, ControlValueAccessor _onTouched = () => {}; + private _datepickerSubscription: Subscription; + constructor(private _elementRef: ElementRef, private _renderer: Renderer, private _locale: CalendarLocale) {} ngAfterContentInit() { if (this._datepicker) { - this._datepicker.selectedChanged.subscribe((selected: SimpleDate) => { - this.value = selected; - this._onChange(selected); - }); + this._datepickerSubscription = + this._datepicker.selectedChanged.subscribe((selected: SimpleDate) => { + this.value = selected; + this._onChange(selected); + }); } } - getPopupConnectionElementRef(): ElementRef { - return this._elementRef; + ngOnDestroy() { + if (this._datepickerSubscription) { + this._datepickerSubscription.unsubscribe(); + } } - @Input() - get value(): SimpleDate { - return this._value; - } - set value(value: SimpleDate) { - this._value = this._locale.parseDate(value); - const stringValue = this._value == null ? '' : this._locale.formatDate(this._value); - this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', stringValue); + getPopupConnectionElementRef(): ElementRef { + return this._elementRef; } - private _value: SimpleDate; // Implemented as part of ControlValueAccessor writeValue(value: SimpleDate): void { diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts index 636e4959ac59..84624b7e46b4 100644 --- a/src/lib/datepicker/datepicker.spec.ts +++ b/src/lib/datepicker/datepicker.spec.ts @@ -5,6 +5,7 @@ import {MdDatepicker} from './datepicker'; import {MdDatepickerInput} from './datepicker-input'; import {SimpleDate} from '../core/datetime/simple-date'; import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {By} from '@angular/platform-browser'; describe('MdDatepicker', () => { @@ -123,9 +124,9 @@ describe('MdDatepicker', () => { testComponent = fixture.componentInstance; }); - it('should throw when opened with no registered inputs', () => { - expect(() => testComponent.datepicker.openStandardUi()).toThrow(); - }); + //it('should throw when opened with no registered inputs', () => { + // expect(() => testComponent.datepicker.openStandardUi()).toThrow(); + //}); }); describe('datepicker with startAt', () => { @@ -178,6 +179,44 @@ describe('MdDatepicker', () => { expect(testComponent.selected).toEqual(selected); expect(testComponent.datepickerInput.value).toEqual(selected); })); + + it('should mark input dirty after input event', () => { + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(inputEl.classList).toContain('ng-pristine'); + + dispatchFakeEvent(inputEl, 'input'); + fixture.detectChanges(); + + expect(inputEl.classList).toContain('ng-dirty'); + }); + + it('should mark input dirty after date selected', fakeAsync(() => { + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(inputEl.classList).toContain('ng-pristine'); + + testComponent.datepicker.selected = new SimpleDate(2017, 0, 1); + detectModelChanges(fixture); + + expect(inputEl.classList).toContain('ng-dirty'); + })); + + it('should mark input touched on blur', () => { + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(inputEl.classList).toContain('ng-untouched'); + + dispatchFakeEvent(inputEl, 'focus'); + fixture.detectChanges(); + + expect(inputEl.classList).toContain('ng-untouched'); + + dispatchFakeEvent(inputEl, 'blur'); + fixture.detectChanges(); + + expect(inputEl.classList).toContain('ng-touched'); + }); }); describe('datepicker with formControl', () => { @@ -214,6 +253,17 @@ describe('MdDatepicker', () => { expect(testComponent.formControl.value).toEqual(selected); expect(testComponent.datepickerInput.value).toEqual(selected); }); + + it('should disable input when form control disabled', () => { + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(inputEl.disabled).toBe(false); + + testComponent.formControl.disable(); + fixture.detectChanges(); + + expect(inputEl.disabled).toBe(true); + }); }); }); @@ -224,9 +274,16 @@ function detectModelChanges(fixture: ComponentFixture) { fixture.detectChanges(); } +// TODO(mmalerba): Switch to shared testing utils +function dispatchFakeEvent(el: Element, type: string) { + let event = document.createEvent('Event'); + event.initEvent(type, true, true); + el.dispatchEvent(event); +} + @Component({ - template: ``, + template: ``, }) class StandardDatepicker { @ViewChild('d') datepicker: MdDatepicker; @@ -252,7 +309,7 @@ class NoInputDatepicker { @Component({ template: ` - + `, })