From 4e419855adcb0fa5001914905c6fc39d89db67ad Mon Sep 17 00:00:00 2001 From: vivian-hu <41269630+vivian-hu@users.noreply.github.com> Date: Thu, 30 Aug 2018 07:02:48 -0700 Subject: [PATCH] feat(form-field): support native select element (#12707) --- .gitignore | 2 + src/demo-app/input/input-demo.html | 4 +- src/demo-app/select/select-demo.html | 161 ++++++++++++++++++ src/demo-app/select/select-demo.ts | 16 +- src/lib/form-field/form-field.md | 6 +- src/lib/input/input.scss | 26 +++ src/lib/input/input.spec.ts | 137 ++++++++++++++- src/lib/input/input.ts | 22 ++- src/lib/select/select.md | 28 ++- .../select-disabled-example.html | 30 +++- .../select-error-state-matcher-example.html | 14 ++ .../select-error-state-matcher-example.ts | 10 ++ .../select-form/select-form-example.html | 14 +- .../select-form/select-form-example.ts | 12 ++ .../select-hint-error-example.html | 17 ++ .../select-hint-error-example.ts | 1 + .../select-optgroup-example.html | 15 ++ .../select-overview-example.html | 11 ++ .../select-reset/select-reset-example.html | 13 ++ 19 files changed, 510 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 1bf2d290c08..66b8148a136 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 30f0bdcbdad..d5cd364c7cc 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 2b2cf07d1e1..b92727f38e8 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 @@ Space above cards:
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 56f0305af7c..dabde38fca5 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 e431374ed1c..a26fadb3ddc 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 330dfeb82e7..abf1aa7b985 100644 --- a/src/lib/input/input.ts +++ b/src/lib/input/input.ts @@ -64,7 +64,8 @@ export const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof 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: { /** @@ -78,7 +79,7 @@ export const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof 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()', @@ -99,6 +100,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 @@ -257,6 +261,7 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl< } this._isServer = !this._platform.isBrowser; + this._isNativeSelect = this._elementRef.nativeElement.nodeName.toLowerCase() === 'select'; } ngOnInit() { @@ -362,7 +367,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 788f61ef2ab..2fa6fc8f5f9 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 f6689b67001..ab8fcbb9419 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 5341d852311..945b3634ca7 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 484c7282bc4..7f04d1f7cd4 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 aedf9673289..b72b6581f9e 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 fc7f443075c..d7c9653725c 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 f40d09b3d9f..b5ac6759241 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 2467483619c..74bb7cad821 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 95459267599..5e6f1387f49 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 9d8e2c0ffd8..235939f9a02 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 + +