+ [floatPlaceholder]="floatPlaceholder" #drinkControl="ngModel">
{{ drink.viewValue }}
@@ -27,6 +27,15 @@
Touched: {{ drinkControl.touched }}
Dirty: {{ drinkControl.dirty }}
Status: {{ drinkControl.control?.status }}
+
+
+
+
+
diff --git a/src/demo-app/select/select-demo.ts b/src/demo-app/select/select-demo.ts
index 0b369e0c137b..b731087b5624 100644
--- a/src/demo-app/select/select-demo.ts
+++ b/src/demo-app/select/select-demo.ts
@@ -14,6 +14,7 @@ export class SelectDemo {
showSelect = false;
currentDrink: string;
latestChangeEvent: MdSelectChange;
+ floatPlaceholder: string = 'auto';
foodControl = new FormControl('pizza-1');
foods = [
diff --git a/src/lib/select/select.html b/src/lib/select/select.html
index 4e36b2187800..d3ae2a6ca256 100644
--- a/src/lib/select/select.html
+++ b/src/lib/select/select.html
@@ -1,9 +1,14 @@
- {{ placeholder }}
+ {{ placeholder }}
{{ selected?.viewValue }}
+
diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts
index 59c2e5de58da..ab82b27c9634 100644
--- a/src/lib/select/select.spec.ts
+++ b/src/lib/select/select.spec.ts
@@ -11,7 +11,7 @@ import {
} from '@angular/core';
import {MdSelectModule} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
-import {MdSelect} from './select';
+import {MdSelect, MdSelectFloatPlaceholderType} from './select';
import {MdOption} from '../core/option/option';
import {Dir} from '../core/rtl/dir';
import {
@@ -35,6 +35,7 @@ describe('MdSelect', () => {
SelectWithChangeEvent,
CustomSelectAccessor,
CompWithCustomSelect,
+ FloatPlaceholderSelect,
SelectWithErrorSibling,
ThrowsErrorOnInit,
BasicSelectOnPush
@@ -590,12 +591,12 @@ describe('MdSelect', () => {
});
it('should float the placeholder when the panel is open and unselected', () => {
- expect(fixture.componentInstance.select._placeholderState)
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to initially have a normal position.');
trigger.click();
fixture.detectChanges();
- expect(fixture.componentInstance.select._placeholderState)
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('floating-ltr', 'Expected placeholder to animate up to floating position.');
const backdrop =
@@ -603,7 +604,7 @@ describe('MdSelect', () => {
backdrop.click();
fixture.detectChanges();
- expect(fixture.componentInstance.select._placeholderState)
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to animate back down to normal position.');
});
@@ -616,7 +617,7 @@ describe('MdSelect', () => {
expect(placeholderEl.classList)
.toContain('mat-floating-placeholder', 'Expected placeholder to display as floating.');
- expect(fixture.componentInstance.select._placeholderState)
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected animation state to be empty to avoid animation.');
});
@@ -625,7 +626,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();
- expect(fixture.componentInstance.select._placeholderState).toEqual('floating-rtl');
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState())
+ .toEqual('floating-rtl');
});
@@ -1285,6 +1287,39 @@ describe('MdSelect', () => {
});
});
+ describe('floatPlaceholder option', () => {
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FloatPlaceholderSelect);
+ });
+
+ it('should be able to disable the floating placeholder', () => {
+ let placeholder = fixture.debugElement.query(By.css('.mat-select-placeholder')).nativeElement;
+
+ fixture.componentInstance.floatPlaceholder = 'never';
+ fixture.detectChanges();
+
+ expect(placeholder.style.visibility).toBe('visible');
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState()).toBeFalsy();
+
+ fixture.componentInstance.control.setValue('pizza-1');
+ fixture.detectChanges();
+
+ expect(placeholder.style.visibility).toBe('hidden');
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState()).toBeFalsy();
+ });
+
+ it('should be able to always float the placeholder', () => {
+ expect(fixture.componentInstance.control.value).toBeFalsy();
+
+ fixture.componentInstance.floatPlaceholder = 'always';
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.select._getPlaceholderAnimationState()).toBe('floating-ltr');
+ });
+ });
+
describe('with OnPush change detection', () => {
let fixture: ComponentFixture;
let trigger: HTMLElement;
@@ -1309,6 +1344,7 @@ describe('MdSelect', () => {
});
});
+
@Component({
selector: 'basic-select',
template: `
@@ -1529,6 +1565,29 @@ class BasicSelectOnPush {
@ViewChildren(MdOption) options: QueryList;
}
+@Component({
+ selector: 'floating-placeholder-select',
+ template: `
+
+
+ {{ food.viewValue }}
+
+
+ `,
+})
+class FloatPlaceholderSelect {
+ floatPlaceholder: MdSelectFloatPlaceholderType = 'auto';
+ control = new FormControl();
+ foods: any[] = [
+ { value: 'steak-0', viewValue: 'Steak' },
+ { value: 'pizza-1', viewValue: 'Pizza' },
+ { value: 'tacos-2', viewValue: 'Tacos'}
+ ];
+
+ @ViewChild(MdSelect) select: MdSelect;
+}
+
/**
* TODO: Move this to core testing utility until Angular has event faking
diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts
index c1c0a3e37978..1a92699e5437 100644
--- a/src/lib/select/select.ts
+++ b/src/lib/select/select.ts
@@ -73,6 +73,9 @@ export class MdSelectChange {
constructor(public source: MdSelect, public value: any) { }
}
+/** Allowed values for the floatPlaceholder option. */
+export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
+
@Component({
moduleId: module.id,
selector: 'md-select, mat-select',
@@ -128,7 +131,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
private _placeholder: string;
/** The animation state of the placeholder. */
- _placeholderState = '';
+ private _placeholderState = '';
/**
* The width of the trigger. Must be saved to set the min width of the overlay panel
@@ -226,6 +229,14 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
get required() { return this._required; }
set required(value: any) { this._required = coerceBooleanProperty(value); }
+ /** Whether to float the placeholder text. */
+ @Input()
+ get floatPlaceholder(): MdSelectFloatPlaceholderType { return this._floatPlaceholder; }
+ set floatPlaceholder(value: MdSelectFloatPlaceholderType) {
+ this._floatPlaceholder = value || 'auto';
+ }
+ private _floatPlaceholder: MdSelectFloatPlaceholderType = 'auto';
+
/** Event emitted when the select has been opened. */
@Output() onOpen: EventEmitter = new EventEmitter();
@@ -280,7 +291,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
return;
}
this._calculateOverlayPosition();
- this._placeholderState = this._isRtl() ? 'floating-rtl' : 'floating-ltr';
+ this._placeholderState = this._floatPlaceholderState();
this._panelOpen = true;
}
@@ -588,6 +599,28 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
return clampValue(0, optimalScrollPosition, maxScroll);
}
+ /**
+ * Figures out the appropriate animation state for the placeholder.
+ */
+ _getPlaceholderAnimationState(): string {
+ if (this.floatPlaceholder === 'never') {
+ return '';
+ }
+
+ if (this.floatPlaceholder === 'always') {
+ return this._floatPlaceholderState();
+ }
+
+ return this._placeholderState;
+ }
+
+ /**
+ * Determines the CSS `visibility` of the placeholder element.
+ */
+ _getPlaceholderVisibility(): 'visible'|'hidden' {
+ return (this.floatPlaceholder !== 'never' || !this.selected) ? 'visible' : 'hidden';
+ }
+
/**
* Calculates the y-offset of the select's overlay panel in relation to the
* top start corner of the trigger. It has to be adjusted in order for the
@@ -699,6 +732,10 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
return `50% ${originY}px 0px`;
}
+ /** Figures out the floating placeholder state value. */
+ private _floatPlaceholderState(): string {
+ return this._isRtl() ? 'floating-rtl' : 'floating-ltr';
+ }
}
/** Clamps a value n between min and max values. */