diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts index 9fbfec2b55b..5a885b1bb95 100644 --- a/src/lib/core/core.ts +++ b/src/lib/core/core.ts @@ -103,6 +103,13 @@ export {MdCommonModule} from './common-behaviors/common-module'; // Datetime export * from './datetime/index'; +// Placeholder +export { + FloatPlaceholderType, + PlaceholderOptions, + MD_PLACEHOLDER_GLOBAL_OPTIONS +} from './placeholder/placeholder-options'; + @NgModule({ imports: [ MdLineModule, diff --git a/src/lib/core/placeholder/placeholder-options.ts b/src/lib/core/placeholder/placeholder-options.ts new file mode 100644 index 00000000000..1d354f8efdf --- /dev/null +++ b/src/lib/core/placeholder/placeholder-options.ts @@ -0,0 +1,12 @@ +import { InjectionToken } from '@angular/core'; + +/** InjectionToken that can be used to specify the global placeholder options. */ +export const MD_PLACEHOLDER_GLOBAL_OPTIONS = + new InjectionToken('md-placeholder-global-options'); + +/** Type for the available floatPlaceholder values. */ +export type FloatPlaceholderType = 'always' | 'never' | 'auto'; + +export interface PlaceholderOptions { + float?: FloatPlaceholderType; +} diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts index f5693902a12..4a49411fac1 100644 --- a/src/lib/input/input-container.spec.ts +++ b/src/lib/input/input-container.spec.ts @@ -22,6 +22,7 @@ import { getMdInputContainerMissingMdInputError, getMdInputContainerPlaceholderConflictError } from './input-container-errors'; +import {MD_PLACEHOLDER_GLOBAL_OPTIONS} from '../core/placeholder/placeholder-options'; describe('MdInputContainer', function () { @@ -82,6 +83,29 @@ describe('MdInputContainer', function () { 'Expected MdInputContainer to set floatingLabel to auto by default.'); }); + it('should default to global floating placeholder type', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ + FormsModule, + MdInputModule, + NoopAnimationsModule + ], + declarations: [ + MdInputContainerBaseTestController + ], + providers: [{ provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' } }] + }); + + let fixture = TestBed.createComponent(MdInputContainerBaseTestController); + fixture.detectChanges(); + + let inputContainer = fixture.debugElement.query(By.directive(MdInputContainer)) + .componentInstance as MdInputContainer; + expect(inputContainer.floatPlaceholder).toBe('always', + 'Expected MdInputContainer to set floatingLabel to always from global option.'); + }); + it('should not be treated as empty if type is date', inject([Platform], (platform: Platform) => { if (!(platform.TRIDENT || platform.FIREFOX)) { diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index 41d30d16b46..96a966c5712 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -16,7 +16,8 @@ import { Renderer2, Self, ViewChild, - ViewEncapsulation + ViewEncapsulation, + Inject } from '@angular/core'; import {animate, state, style, transition, trigger} from '@angular/animations'; import {coerceBooleanProperty} from '../core'; @@ -28,7 +29,11 @@ import { getMdInputContainerPlaceholderConflictError, getMdInputContainerUnsupportedTypeError } from './input-container-errors'; - +import { + FloatPlaceholderType, + PlaceholderOptions, + MD_PLACEHOLDER_GLOBAL_OPTIONS +} from '../core/placeholder/placeholder-options'; // Invalid input type. Using one of these will throw an MdInputContainerUnsupportedTypeError. const MD_INPUT_INVALID_TYPES = [ @@ -44,9 +49,6 @@ const MD_INPUT_INVALID_TYPES = [ 'submit' ]; -/** Type for the available floatPlaceholder values. */ -export type FloatPlaceholderType = 'always' | 'never' | 'auto'; - let nextUniqueId = 0; @@ -302,7 +304,10 @@ export class MdInputDirective { }, encapsulation: ViewEncapsulation.None, }) + export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterContentChecked { + private _placeholderOptions: PlaceholderOptions; + /** Alignment of the input container's content. */ @Input() align: 'start' | 'end' = 'start'; @@ -347,9 +352,9 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC @Input() get floatPlaceholder() { return this._floatPlaceholder; } set floatPlaceholder(value: FloatPlaceholderType) { - this._floatPlaceholder = value || 'auto'; + this._floatPlaceholder = value || this._placeholderOptions.float || 'auto'; } - private _floatPlaceholder: FloatPlaceholderType = 'auto'; + private _floatPlaceholder: FloatPlaceholderType; /** Reference to the input's underline element. */ @ViewChild('underline') underlineRef: ElementRef; @@ -368,7 +373,13 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC constructor( public _elementRef: ElementRef, - private _changeDetectorRef: ChangeDetectorRef) { } + private _changeDetectorRef: ChangeDetectorRef, + @Optional() private _parentForm: NgForm, + @Optional() private _parentFormGroup: FormGroupDirective, + @Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) { + this._placeholderOptions = placeholderOptions ? placeholderOptions : {}; + this.floatPlaceholder = this._placeholderOptions.float || 'auto'; + } ngAfterContentInit() { this._validateInputChild(); diff --git a/src/lib/input/input.md b/src/lib/input/input.md index ec083cfd947..93374379ef9 100644 --- a/src/lib/input/input.md +++ b/src/lib/input/input.md @@ -60,6 +60,22 @@ A placeholder for the input can be specified in one of two ways: either using th attribute on the `input` or `textarea`, or using an `md-placeholder` element in the `md-input-container`. Using both will raise an error. +Global default placeholder options can be specified by setting the `MD_PLACEHOLDER_GLOBAL_OPTIONS` provider. This setting will apply to all components that support the floating placeholder. + +```ts +@NgModule({ + providers: [ + {provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' }} + ] +}) +``` + +Here are the available global options: + +| Name | Type | Values | Description | +| --------------- | ------- | ------------------- | ----------------------------------------- | +| float | string | auto, always, never | The default placeholder float behavior. | + ### Prefix and Suffix HTML can be included before, and after the input tag, as prefix or suffix. It will be underlined as diff --git a/src/lib/select/select.md b/src/lib/select/select.md index 11098c03b26..0379498417c 100644 --- a/src/lib/select/select.md +++ b/src/lib/select/select.md @@ -62,6 +62,22 @@ It's possible to turn off the placeholder's floating animation using the `floatP ``` +Global default placeholder options can be specified by setting the `MD_PLACEHOLDER_GLOBAL_OPTIONS` provider. This setting will apply to all components that support the floating placeholder. + +```ts +@NgModule({ + providers: [ + {provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' }} + ] +}) +``` + +Here are the available global options: + +| Name | Type | Values | Description | +| --------------- | ------- | ------------------- | ----------------------------------------- | +| float | string | auto, always, never | The default placeholder float behavior. | + #### Keyboard interaction: - DOWN_ARROW: Focus next option - UP_ARROW: Focus previous option diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 3d42521ac3d..cf6d515a255 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -12,7 +12,7 @@ import { import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MdSelectModule} from './index'; import {OverlayContainer} from '../core/overlay/overlay-container'; -import {MdSelect, MdSelectFloatPlaceholderType} from './select'; +import {MdSelect} from './select'; import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './select-errors'; import {MdOption} from '../core/option/option'; import {Dir} from '../core/rtl/dir'; @@ -25,6 +25,10 @@ import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {dispatchFakeEvent, dispatchKeyboardEvent} from '../core/testing/dispatch-events'; import {wrappedErrorMessage} from '../core/testing/wrapped-error-message'; import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; +import { + FloatPlaceholderType, + MD_PLACEHOLDER_GLOBAL_OPTIONS +} from '../core/placeholder/placeholder-options'; describe('MdSelect', () => { @@ -1798,6 +1802,30 @@ describe('MdSelect', () => { expect(fixture.componentInstance.select._getPlaceholderAnimationState()).toBe('floating-ltr'); }); + + it ('should default to global floating placeholder type', () => { + fixture.destroy(); + + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ + MdSelectModule, + ReactiveFormsModule, + FormsModule, + NoopAnimationsModule + ], + declarations: [ + FloatPlaceholderSelect + ], + providers: [{ provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' } }] + }); + + fixture = TestBed.createComponent(FloatPlaceholderSelect); + fixture.componentInstance.floatPlaceholder = null; + fixture.detectChanges(); + + expect(fixture.componentInstance.select.floatPlaceholder).toBe('always'); + }); }); describe('with OnPush change detection', () => { @@ -2419,7 +2447,7 @@ class BasicSelectOnPushPreselected { `, }) class FloatPlaceholderSelect { - floatPlaceholder: MdSelectFloatPlaceholderType = 'auto'; + floatPlaceholder: FloatPlaceholderType = 'auto'; control = new FormControl(); foods: any[] = [ { value: 'steak-0', viewValue: 'Steak' }, diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index f37d2dd5a7a..7cbef2ad83e 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -16,6 +16,7 @@ import { ChangeDetectorRef, Attribute, OnInit, + Inject } from '@angular/core'; import {MdOption, MdOptionSelectionChange} from '../core/option/option'; import {ENTER, SPACE, UP_ARROW, DOWN_ARROW, HOME, END} from '../core/keyboard/keycodes'; @@ -36,7 +37,11 @@ import 'rxjs/add/operator/startWith'; import 'rxjs/add/operator/filter'; import {CanColor, mixinColor} from '../core/common-behaviors/color'; import {CanDisable} from '../core/common-behaviors/disabled'; - +import { + FloatPlaceholderType, + PlaceholderOptions, + MD_PLACEHOLDER_GLOBAL_OPTIONS} +from '../core/placeholder/placeholder-options'; /** * The following style constants are necessary to save here in order @@ -94,16 +99,12 @@ export class MdSelectChange { constructor(public source: MdSelect, public value: any) { } } -/** Allowed values for the floatPlaceholder option. */ -export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto'; - // Boilerplate for applying mixins to MdSelect. export class MdSelectBase { constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {} } export const _MdSelectMixinBase = mixinColor(MdSelectBase, 'primary'); - @Component({ moduleId: module.id, selector: 'md-select, mat-select', @@ -170,6 +171,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Tab index for the element. */ private _tabIndex: number; + /** Deals with configuring placeholder options */ + private _placeholderOptions: PlaceholderOptions; + /** * The width of the trigger. Must be saved to set the min width of the overlay panel * and the width of the selected value. @@ -275,11 +279,11 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Whether to float the placeholder text. */ @Input() - get floatPlaceholder(): MdSelectFloatPlaceholderType { return this._floatPlaceholder; } - set floatPlaceholder(value: MdSelectFloatPlaceholderType) { - this._floatPlaceholder = value || 'auto'; + get floatPlaceholder(): FloatPlaceholderType { return this._floatPlaceholder; } + set floatPlaceholder(value: FloatPlaceholderType) { + this._floatPlaceholder = value || this._placeholderOptions.float || 'auto'; } - private _floatPlaceholder: MdSelectFloatPlaceholderType = 'auto'; + private _floatPlaceholder: FloatPlaceholderType; /** Tab index for the select element. */ @Input() @@ -310,13 +314,15 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Event emitted when the selected value has been changed by the user. */ @Output() change: EventEmitter = new EventEmitter(); - constructor(private _viewportRuler: ViewportRuler, - private _changeDetectorRef: ChangeDetectorRef, - renderer: Renderer2, - elementRef: ElementRef, - @Optional() private _dir: Dir, - @Self() @Optional() public _control: NgControl, - @Attribute('tabindex') tabIndex: string) { + constructor( + private _viewportRuler: ViewportRuler, + private _changeDetectorRef: ChangeDetectorRef, + renderer: Renderer2, + elementRef: ElementRef, + @Optional() private _dir: Dir, + @Self() @Optional() public _control: NgControl, + @Attribute('tabindex') tabIndex: string, + @Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) { super(renderer, elementRef); if (this._control) { @@ -324,6 +330,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On } this._tabIndex = parseInt(tabIndex) || 0; + + this._placeholderOptions = placeholderOptions ? placeholderOptions : {}; + this.floatPlaceholder = this._placeholderOptions.float || 'auto'; } ngOnInit() {