diff --git a/.gitignore b/.gitignore index 1bf2d290c08a..66b8148a1362 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ node_modules # IDEs /.idea /.vscode +/*.iml # misc .DS_Store @@ -38,6 +39,7 @@ npm-debug.log testem.log /.chrome /.git +/.firebase # schematics /src/lib/schematics/**/*.js diff --git a/src/demo-app/input/input-demo.html b/src/demo-app/input/input-demo.html index 30f0bdcbdad9..d5cd364c7cc2 100644 --- a/src/demo-app/input/input-demo.html +++ b/src/demo-app/input/input-demo.html @@ -4,7 +4,7 @@
Company (disabled) - + @@ -28,7 +28,7 @@ Address 2 - +

diff --git a/src/demo-app/select/select-demo.html b/src/demo-app/select/select-demo.html index 2b2cf07d1e1c..b92727f38e8b 100644 --- a/src/demo-app/select/select-demo.html +++ b/src/demo-app/select/select-demo.html @@ -1,3 +1,163 @@ + + + + Native Select + + +

Basic

+ + Select your car + + +

Disabled and required

+ + Select your car (disabled) + + +

Floating label

+ + Float with value + + + + Not float when empty + + + + Float with no value, but with label + + + + Float with no value, but with html + + +

Looks

+ + Legacy + + + + Standard + + + + Fill + + + + Outline + + +

Option group

+ + + +

Place holder

+ + + +

Error message, hint, form sumbit

+ + Select your car (required) + + + This field is required + + You can pick up your favorite car here + + +

Error message with errorStateMatcher

+ + Select your car + + + This field is required + + You can pick up your favorite car here + + + +
+
+ + +mat-select Space above cards:
@@ -240,3 +400,4 @@
This div is for testing scrolled selects.
+ diff --git a/src/demo-app/select/select-demo.ts b/src/demo-app/select/select-demo.ts index 56f0305af7c5..dabde38fca54 100644 --- a/src/demo-app/select/select-demo.ts +++ b/src/demo-app/select/select-demo.ts @@ -7,9 +7,18 @@ */ import {Component} from '@angular/core'; -import {FormControl} from '@angular/forms'; -import {MatSelectChange} from '@angular/material'; +import {FormControl, Validators} from '@angular/forms'; +import {ErrorStateMatcher, MatSelectChange} from '@angular/material'; +/** Error any time control is invalid */ +export class MyErrorStateMatcher implements ErrorStateMatcher { + isErrorState(control: FormControl | null): boolean { + if (control) { + return control.invalid; + } + return false; + } +} @Component({ moduleId: module.id, @@ -37,6 +46,7 @@ export class SelectDemo { drinksTheme = 'primary'; pokemonTheme = 'primary'; compareByValue = true; + selectFormControl = new FormControl('', Validators.required); foods = [ {value: null, viewValue: 'None'}, @@ -137,6 +147,8 @@ export class SelectDemo { return o1 === o2; } + matcher = new MyErrorStateMatcher(); + toggleSelected() { this.currentAppearanceValue = this.currentAppearanceValue ? null : this.digimon[0].value; } diff --git a/src/lib/form-field/form-field.md b/src/lib/form-field/form-field.md index e431374ed1ca..a26fadb3ddc0 100644 --- a/src/lib/form-field/form-field.md +++ b/src/lib/form-field/form-field.md @@ -7,7 +7,8 @@ In this document, "form field" refers to the wrapper component ` (e.g. the input, textarea, select, etc.) The following Angular Material components are designed to work inside a ``: -* [`` & ` + ` }) class MatInputTextareaWithBindings { @@ -1726,3 +1804,58 @@ class AutosizeTextareaInATab {} ` }) class AutosizeTextareaInAStep {} + +@Component({ + template: ` + + + ` +}) +class MatInputSelect { + disabled: boolean; + required: boolean; +} + +@Component({ + template: ` + + + ` +}) +class MatInputSelectWithNoLabelNoValue {} + +@Component({ + template: ` + + + ` +}) +class MatInputSelectWithLabel {} + +@Component({ + template: ` + + + ` +}) +class MatInputSelectWithInnerHtml {} diff --git a/src/lib/input/input.ts b/src/lib/input/input.ts index 1fd02d06631c..13419d519219 100644 --- a/src/lib/input/input.ts +++ b/src/lib/input/input.ts @@ -58,7 +58,8 @@ export const _MatInputMixinBase = mixinErrorState(MatInputBase); /** Directive that allows a native input to work inside a `MatFormField`. */ @Directive({ - selector: `input[matInput], textarea[matInput]`, + selector: `input[matInput], textarea[matInput], select[matNativeControl], + input[matNativeControl], textarea[matNativeControl]`, exportAs: 'matInput', host: { /** @@ -72,7 +73,7 @@ export const _MatInputMixinBase = mixinErrorState(MatInputBase); '[attr.placeholder]': 'placeholder', '[disabled]': 'disabled', '[required]': 'required', - '[readonly]': 'readonly', + '[attr.readonly]': 'readonly && !_isNativeSelect || null', '[attr.aria-describedby]': '_ariaDescribedby || null', '[attr.aria-invalid]': 'errorState', '[attr.aria-required]': 'required.toString()', @@ -93,6 +94,9 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< /** Whether the component is being rendered on the server. */ _isServer = false; + /** Whether the component is a native html select. */ + _isNativeSelect = false; + /** * Implemented as part of MatFormFieldControl. * @docs-private @@ -251,6 +255,7 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< } this._isServer = !this._platform.isBrowser; + this._isNativeSelect = this._elementRef.nativeElement.nodeName.toLowerCase() === 'select'; } ngOnInit() { @@ -356,7 +361,18 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< * Implemented as part of MatFormFieldControl. * @docs-private */ - get shouldLabelFloat(): boolean { return this.focused || !this.empty; } + get shouldLabelFloat(): boolean { + if (this._isNativeSelect) { + // For a single-selection ``, the label *always* floats to avoid + // overlapping the label with the options. + const selectElement = this._elementRef.nativeElement; + return selectElement.multiple || !this.empty || selectElement.options[0].label || + this.focused; + } else { + return this.focused || !this.empty; + } + } /** * Implemented as part of MatFormFieldControl. diff --git a/src/lib/select/select.md b/src/lib/select/select.md index 788f61ef2ab7..2fa6fc8f5f90 100644 --- a/src/lib/select/select.md +++ b/src/lib/select/select.md @@ -8,6 +8,14 @@ To add options to the select, add `` elements to the ``. has a `value` property that can be used to set the value that will be selected if the user chooses this option. The content of the `` is what will be shown to the user. +Angular Material also supports use of the native `` element. + ### Getting and setting the select value @@ -17,7 +25,7 @@ forms. -The `` also supports all of the form directives from the core `FormsModule` (`NgModel`) and +Both`` and ``, `` also supports a `compareWith` function. (Additional information about using a custom `compareWith` function can be found in the @@ -27,7 +35,7 @@ function can be found in the ### Form field features -There are a number of `` features that can be used with any ``. These +There are a number of `` features that can be used with both `` or `` and the ` + diff --git a/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.html b/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.html index f6689b670019..ab8fcbb94197 100644 --- a/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.html +++ b/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.html @@ -1,3 +1,4 @@ +

mat-select

Clear @@ -10,3 +11,16 @@ Your selection is invalid + +

native html select

+ + + You must make a selection + + Your selection is invalid + + diff --git a/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.ts b/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.ts index 5341d8523114..945b3634ca7c 100644 --- a/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.ts +++ b/src/material-examples/select-error-state-matcher/select-error-state-matcher-example.ts @@ -22,5 +22,15 @@ export class SelectErrorStateMatcherExample { Validators.pattern('valid'), ]); + selectFormControl = new FormControl('valid', [ + Validators.required, + Validators.pattern('valid'), + ]); + + nativeSelectFormControl = new FormControl('valid', [ + Validators.required, + Validators.pattern('valid'), + ]); + matcher = new MyErrorStateMatcher(); } diff --git a/src/material-examples/select-form/select-form-example.html b/src/material-examples/select-form/select-form-example.html index 484c7282bc44..7f04d1f7cd4a 100644 --- a/src/material-examples/select-form/select-form-example.html +++ b/src/material-examples/select-form/select-form-example.html @@ -1,4 +1,5 @@
+

mat-select

@@ -6,6 +7,15 @@ - -

Selected value: {{selectedValue}}

+

Selected food: {{selectedValue}}

+

native html select

+ + + +

Selected car: {{selectedCar}}

diff --git a/src/material-examples/select-form/select-form-example.ts b/src/material-examples/select-form/select-form-example.ts index aedf96732899..b72b6581f9ea 100644 --- a/src/material-examples/select-form/select-form-example.ts +++ b/src/material-examples/select-form/select-form-example.ts @@ -5,6 +5,11 @@ export interface Food { viewValue: string; } +export interface Car { + value: string; + viewValue: string; +} + /** * @title Select in a form */ @@ -15,10 +20,17 @@ export interface Food { }) export class SelectFormExample { selectedValue: string; + selectedCar: string; foods: Food[] = [ {value: 'steak-0', viewValue: 'Steak'}, {value: 'pizza-1', viewValue: 'Pizza'}, {value: 'tacos-2', viewValue: 'Tacos'} ]; + + cars: Car[] = [ + {value: 'volvo', viewValue: 'Volvo'}, + {value: 'saab', viewValue: 'Saab'}, + {value: 'mercedes', viewValue: 'Mercedes'} + ]; } diff --git a/src/material-examples/select-hint-error/select-hint-error-example.html b/src/material-examples/select-hint-error/select-hint-error-example.html index fc7f443075ce..d7c9653725c2 100644 --- a/src/material-examples/select-hint-error/select-hint-error-example.html +++ b/src/material-examples/select-hint-error/select-hint-error-example.html @@ -1,3 +1,4 @@ +

mat select

-- @@ -8,3 +9,19 @@ Please choose an animal {{animalControl.value?.sound}} + +

native html select

+ + Select your car (required) + + + This field is required + + You can pick up your favorite car here + + diff --git a/src/material-examples/select-hint-error/select-hint-error-example.ts b/src/material-examples/select-hint-error/select-hint-error-example.ts index f40d09b3d9fb..b5ac67592419 100644 --- a/src/material-examples/select-hint-error/select-hint-error-example.ts +++ b/src/material-examples/select-hint-error/select-hint-error-example.ts @@ -14,6 +14,7 @@ export interface Animal { }) export class SelectHintErrorExample { animalControl = new FormControl('', [Validators.required]); + selectFormControl = new FormControl('', Validators.required); animals: Animal[] = [ {name: 'Dog', sound: 'Woof!'}, {name: 'Cat', sound: 'Meow!'}, diff --git a/src/material-examples/select-optgroup/select-optgroup-example.html b/src/material-examples/select-optgroup/select-optgroup-example.html index 2467483619cb..74bb7cad8216 100644 --- a/src/material-examples/select-optgroup/select-optgroup-example.html +++ b/src/material-examples/select-optgroup/select-optgroup-example.html @@ -1,3 +1,4 @@ +

mat-select

-- None -- @@ -9,3 +10,17 @@ + +

native html select

+ + + diff --git a/src/material-examples/select-overview/select-overview-example.html b/src/material-examples/select-overview/select-overview-example.html index 954592675993..5e6f1387f49f 100644 --- a/src/material-examples/select-overview/select-overview-example.html +++ b/src/material-examples/select-overview/select-overview-example.html @@ -1,3 +1,4 @@ +

Basic mat-select

@@ -5,3 +6,13 @@ + +

Basic native select

+ + + diff --git a/src/material-examples/select-reset/select-reset-example.html b/src/material-examples/select-reset/select-reset-example.html index 9d8e2c0ffd81..235939f9a022 100644 --- a/src/material-examples/select-reset/select-reset-example.html +++ b/src/material-examples/select-reset/select-reset-example.html @@ -1,6 +1,19 @@ +

mat-select

None {{state}} + +

native html select

+ + Select your car + +