diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts index 93512f0b857f..0632f4bbce45 100644 --- a/src/lib/input/input-container.spec.ts +++ b/src/lib/input/input-container.spec.ts @@ -1,9 +1,9 @@ import {async, TestBed, inject} from '@angular/core/testing'; import {Component} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms'; import {By} from '@angular/platform-browser'; import {MdInputModule} from './input'; -import {MdInputContainer} from './input-container'; +import {MdInputContainer, MdInputDirective} from './input-container'; import {Platform} from '../core/platform/platform'; import {PlatformModule} from '../core/platform/index'; import { @@ -43,6 +43,9 @@ describe('MdInputContainer', function () { MdInputContainerWithDisabled, MdInputContainerWithRequired, MdInputContainerWithType, + MdInputContainerWithValueBinding, + MdInputContainerWithFormControl, + MdInputContainerWithStaticPlaceholder, MdInputContainerMissingMdInputTestController ], }); @@ -130,6 +133,40 @@ describe('MdInputContainer', function () { expect(el.classList.contains('md-empty')).toBe(false, 'should not be empty'); })); + it('should update the placeholder when input entered', async(() => { + let fixture = TestBed.createComponent(MdInputContainerWithStaticPlaceholder); + fixture.detectChanges(); + + let inputEl = fixture.debugElement.query(By.css('input')); + let labelEl = fixture.debugElement.query(By.css('label')).nativeElement; + + expect(labelEl.classList).toContain('md-empty'); + expect(labelEl.classList).not.toContain('md-float'); + + // Update the value of the input. + inputEl.nativeElement.value = 'Text'; + + // Fake behavior of the `(input)` event which should trigger a change detection. + fixture.detectChanges(); + + expect(labelEl.classList).not.toContain('md-empty'); + expect(labelEl.classList).not.toContain('md-float'); + })); + + it('should not be empty when the value set before view init', async(() => { + let fixture = TestBed.createComponent(MdInputContainerWithValueBinding); + fixture.detectChanges(); + + let placeholderEl = fixture.debugElement.query(By.css('.md-input-placeholder')).nativeElement; + + expect(placeholderEl.classList).not.toContain('md-empty'); + + fixture.componentInstance.value = ''; + fixture.detectChanges(); + + expect(placeholderEl.classList).toContain('md-empty'); + })); + it('should not treat the number 0 as empty', async(() => { let fixture = TestBed.createComponent(MdInputContainerZeroTestController); fixture.detectChanges(); @@ -143,6 +180,20 @@ describe('MdInputContainer', function () { }); })); + it('should update the value when using FormControl.setValue', () => { + let fixture = TestBed.createComponent(MdInputContainerWithFormControl); + fixture.detectChanges(); + + let input = fixture.debugElement.query(By.directive(MdInputDirective)) + .injector.get(MdInputDirective) as MdInputDirective; + + expect(input.value).toBeFalsy(); + + fixture.componentInstance.formControl.setValue('something'); + + expect(input.value).toBe('something'); + }); + it('should add id', () => { let fixture = TestBed.createComponent(MdInputContainerTextTestController); fixture.detectChanges(); @@ -379,6 +430,13 @@ class MdInputContainerPlaceholderElementTestComponent { placeholder: string = 'Default Placeholder'; } +@Component({ + template: `` +}) +class MdInputContainerWithFormControl { + formControl = new FormControl(); +} + @Component({ template: `` }) @@ -482,6 +540,25 @@ class MdInputContainerZeroTestController { value = 0; } +@Component({ + template: ` + + + ` +}) +class MdInputContainerWithValueBinding { + value: string = 'Initial'; +} + +@Component({ + template: ` + + + + ` +}) +class MdInputContainerWithStaticPlaceholder {} + @Component({ template: ` diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index a74a52af9a7d..bb4df7399df3 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -83,10 +83,10 @@ export class MdHint { '[required]': 'required', '(blur)': '_onBlur()', '(focus)': '_onFocus()', - '(input)': '_onInput()', + '(input)': '_onInput()' } }) -export class MdInputDirective implements AfterContentInit { +export class MdInputDirective { /** Variables used as cache for getters and setters. */ private _type = 'text'; @@ -96,9 +96,6 @@ export class MdInputDirective implements AfterContentInit { private _id: string; private _cachedUid: string; - /** The element's value. */ - value: any; - /** Whether the element is focused or not. */ focused = false; @@ -141,6 +138,10 @@ export class MdInputDirective implements AfterContentInit { } } + /** The input element's value. */ + get value() { return this._elementRef.nativeElement.value; } + set value(value: string) { this._elementRef.nativeElement.value = value; } + /** * Emits an event when the placeholder changes so that the `md-input-container` can re-validate. */ @@ -162,18 +163,9 @@ export class MdInputDirective implements AfterContentInit { constructor(private _elementRef: ElementRef, private _renderer: Renderer, @Optional() public _ngControl: NgControl) { + // Force setter to be called in case id was not specified. this.id = this.id; - - if (this._ngControl && this._ngControl.valueChanges) { - this._ngControl.valueChanges.subscribe((value) => { - this.value = value; - }); - } - } - - ngAfterContentInit() { - this.value = this._elementRef.nativeElement.value; } /** Focuses the input element. */ @@ -183,7 +175,15 @@ export class MdInputDirective implements AfterContentInit { _onBlur() { this.focused = false; } - _onInput() { this.value = this._elementRef.nativeElement.value; } + _onInput() { + // This is a noop function and is used to let Angular know whenever the value changes. + // Angular will run a new change detection each time the `input` event has been dispatched. + // It's necessary that Angular recognizes the value change, because when floatingLabel + // is set to false and Angular forms aren't used, the placeholder won't recognize the + // value changes and will not disappear. + // Listening to the input event wouldn't be necessary when the input is using the + // FormsModule or ReactiveFormsModule, because Angular forms also listens to input events. + } /** Make sure the input is a supported type. */ private _validateType() {