mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(form-field): support native select element (#12707)
This commit is contained in:
committed by
Jeremy Elbourn
parent
53b4af6d2a
commit
4e419855ad
@@ -4,7 +4,7 @@
|
||||
<form>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Company (disabled)</mat-label>
|
||||
<input matInput disabled value="Google">
|
||||
<input matNativeControl disabled value="Google">
|
||||
</mat-form-field>
|
||||
|
||||
<table style="width: 100%" cellspacing="0"><tr>
|
||||
@@ -28,7 +28,7 @@
|
||||
</mat-form-field>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Address 2</mat-label>
|
||||
<textarea matInput></textarea>
|
||||
<textarea matNativeControl></textarea>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<table style="width: 100%" cellspacing="0"><tr>
|
||||
|
||||
@@ -1,3 +1,163 @@
|
||||
|
||||
|
||||
<mat-card class="demo-card demo-basic">
|
||||
<mat-toolbar color="primary">Native Select</mat-toolbar>
|
||||
<mat-card-content>
|
||||
<form>
|
||||
<h4>Basic</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Select your car</mat-label>
|
||||
<select matNativeControl id="mySelectId">
|
||||
<option value="" disabled selected></option>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab" disabled>Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Disabled and required</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Select your car (disabled)</mat-label>
|
||||
<select matNativeControl disabled required>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Floating label</h4>
|
||||
<mat-form-field>
|
||||
<mat-label>Float with value</mat-label>
|
||||
<select matNativeControl>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Not float when empty</mat-label>
|
||||
<select matNativeControl>
|
||||
<option value="" selected></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Float with no value, but with label</mat-label>
|
||||
<select matNativeControl>
|
||||
<option value="" selected label="--select one--"></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Float with no value, but with html</mat-label>
|
||||
<select matNativeControl>
|
||||
<option value="" selected>--select one--</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Looks</h4>
|
||||
<mat-form-field appearance="legacy">
|
||||
<mat-label>Legacy</mat-label>
|
||||
<select matNativeControl required>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="standard">
|
||||
<mat-label>Standard</mat-label>
|
||||
<select matNativeControl required>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Fill</mat-label>
|
||||
<select matNativeControl required>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Outline</mat-label>
|
||||
<select matNativeControl>
|
||||
<option value="volvo">volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Option group</h4>
|
||||
<mat-form-field>
|
||||
<select matNativeControl>
|
||||
<optgroup label="Swedish Cars">
|
||||
<option value="volvo">volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
</optgroup>
|
||||
<optgroup label="German Cars">
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Place holder</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<select matNativeControl placeholder="place holder">
|
||||
<option value="" disabled selected></option>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab" disabled>Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<h4>Error message, hint, form sumbit</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Select your car (required)</mat-label>
|
||||
<select matNativeControl required [formControl]="selectFormControl">
|
||||
<option label="--select something --"></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
<mat-error *ngIf="selectFormControl.hasError('required')">
|
||||
This field is required
|
||||
</mat-error>
|
||||
<mat-hint>You can pick up your favorite car here</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>Error message with errorStateMatcher</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<mat-label>Select your car</mat-label>
|
||||
<select matNativeControl required [formControl]="selectFormControl" [errorStateMatcher]="matcher">
|
||||
<option label="--select something --"></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
<mat-error *ngIf="selectFormControl.hasError('required')">
|
||||
This field is required
|
||||
</mat-error>
|
||||
<mat-hint>You can pick up your favorite car here</mat-hint>
|
||||
</mat-form-field>
|
||||
<button color="primary" mat-raised-button>Submit</button>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
|
||||
<mat-toolbar color="primary">mat-select</mat-toolbar>
|
||||
Space above cards: <input type="number" [formControl]="topHeightCtrl">
|
||||
<button mat-button (click)="showSelect=!showSelect">SHOW SELECT</button>
|
||||
<div [style.height.px]="topHeightCtrl.value"></div>
|
||||
@@ -240,3 +400,4 @@ Space above cards: <input type="number" [formControl]="topHeightCtrl">
|
||||
|
||||
</div>
|
||||
<div style="height: 500px">This div is for testing scrolled selects.</div>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>
|
||||
(e.g. the input, textarea, select, etc.)
|
||||
|
||||
The following Angular Material components are designed to work inside a `<mat-form-field>`:
|
||||
* [`<input matInput>` & `<textarea matInput>`](https://material.angular.io/components/input/overview)
|
||||
* [`<input matNativeControl>` & `<textarea matNativeControl>`](https://material.angular.io/components/input/overview)
|
||||
* [`<select matNativeControl>`](https://material.angular.io/components/select/overview)
|
||||
* [`<mat-select>`](https://material.angular.io/components/select/overview)
|
||||
* [`<mat-chip-list>`](https://material.angular.io/components/chips/overview)
|
||||
|
||||
@@ -41,7 +42,8 @@ want a floating label, add a `<mat-label>` to the `mat-form-field`.
|
||||
### Floating label
|
||||
|
||||
The floating label is a text label displayed on top of the form field control when
|
||||
the control does not contain any text. By default, when text is present the floating label
|
||||
the control does not contain any text or when `<select matNativeControl>` does not show any option text.
|
||||
By default, when text is present the floating label
|
||||
floats above the form field control. The label for a form field can be specified by adding a
|
||||
`mat-label` element.
|
||||
|
||||
|
||||
@@ -121,3 +121,29 @@ textarea.mat-input-element {
|
||||
padding: 2px 0;
|
||||
margin: -2px 0;
|
||||
}
|
||||
|
||||
// Encoded material design select arrow svg
|
||||
/* stylelint-disable max-line-length */
|
||||
$mat-native-select-arrow-svg: 'data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%2210%22%20height%3D%225%22%20viewBox%3D%227%2010%2010%205%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22%230%22%20fill-rule%3D%22evenodd%22%20opacity%3D%22.54%22%20d%3D%22M7%2010l5%205%205-5z%22%2F%3E%3C%2Fsvg%3E';
|
||||
/* stylelint-enable */
|
||||
|
||||
// Remove the native select down arrow and replace it with material design arrow
|
||||
select.mat-input-element {
|
||||
&::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
background-image: url($mat-native-select-arrow-svg);
|
||||
background-repeat: no-repeat;
|
||||
display: inline-flex;
|
||||
box-sizing: border-box;
|
||||
background-position: right center;
|
||||
|
||||
[dir='rtl'] & {
|
||||
background-position: left center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+135
-2
@@ -443,6 +443,26 @@ describe('MatInput without forms', () => {
|
||||
expect(inputEl.disabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('supports the disabled attribute as binding for select', fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
const formFieldEl =
|
||||
fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
|
||||
const selectEl = fixture.debugElement.query(By.css('select')).nativeElement;
|
||||
|
||||
expect(formFieldEl.classList.contains('mat-form-field-disabled'))
|
||||
.toBe(false, `Expected form field not to start out disabled.`);
|
||||
expect(selectEl.disabled).toBe(false);
|
||||
|
||||
fixture.componentInstance.disabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(formFieldEl.classList.contains('mat-form-field-disabled'))
|
||||
.toBe(true, `Expected form field to look disabled after property is set.`);
|
||||
expect(selectEl.disabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('supports the required attribute as binding', fakeAsync(() => {
|
||||
let fixture = createComponent(MatInputWithRequired);
|
||||
fixture.detectChanges();
|
||||
@@ -457,6 +477,20 @@ describe('MatInput without forms', () => {
|
||||
expect(inputEl.required).toBe(true);
|
||||
}));
|
||||
|
||||
it('supports the required attribute as binding for select', fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
const selectEl = fixture.debugElement.query(By.css('select')).nativeElement;
|
||||
|
||||
expect(selectEl.required).toBe(false);
|
||||
|
||||
fixture.componentInstance.required = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(selectEl.required).toBe(true);
|
||||
}));
|
||||
|
||||
it('supports the type attribute as binding', fakeAsync(() => {
|
||||
let fixture = createComponent(MatInputWithType);
|
||||
fixture.detectChanges();
|
||||
@@ -479,6 +513,14 @@ describe('MatInput without forms', () => {
|
||||
expect(textarea).not.toBeNull();
|
||||
}));
|
||||
|
||||
it('supports select', fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
const nativeSelect: HTMLTextAreaElement = fixture.nativeElement.querySelector('select');
|
||||
expect(nativeSelect).not.toBeNull();
|
||||
}));
|
||||
|
||||
it('sets the aria-describedby when a hintLabel is set', fakeAsync(() => {
|
||||
let fixture = createComponent(MatInputHintLabelTestController);
|
||||
|
||||
@@ -577,6 +619,41 @@ describe('MatInput without forms', () => {
|
||||
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
|
||||
}));
|
||||
|
||||
it('should float labels when select has value', fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
|
||||
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
|
||||
}));
|
||||
|
||||
it('should not float labels when select has no value, no option label, ' +
|
||||
'no option innerHtml', fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelectWithNoLabelNoValue);
|
||||
fixture.detectChanges();
|
||||
|
||||
const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
|
||||
expect(formFieldEl.classList).not.toContain('mat-form-field-should-float');
|
||||
}));
|
||||
|
||||
it('should floating labels when select has no value but has option label',
|
||||
fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelectWithLabel);
|
||||
fixture.detectChanges();
|
||||
|
||||
const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
|
||||
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
|
||||
}));
|
||||
|
||||
it('should floating labels when select has no value but has option innerHTML',
|
||||
fakeAsync(() => {
|
||||
const fixture = createComponent(MatInputSelectWithInnerHtml);
|
||||
fixture.detectChanges();
|
||||
|
||||
const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field'))
|
||||
.nativeElement;
|
||||
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
|
||||
}));
|
||||
|
||||
it('should never float the label when floatLabel is set to false', fakeAsync(() => {
|
||||
let fixture = createComponent(MatInputWithDynamicLabel);
|
||||
@@ -1296,7 +1373,7 @@ function createComponent<T>(component: Type<T>,
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<input matInput id="test-id" placeholder="test">
|
||||
<input matNativeControl id="test-id" placeholder="test">
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputWithId {}
|
||||
@@ -1503,7 +1580,8 @@ class MatInputWithDynamicLabel {
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<textarea matInput [rows]="rows" [cols]="cols" [wrap]="wrap" placeholder="Snacks"></textarea>
|
||||
<textarea matNativeControl [rows]="rows" [cols]="cols" [wrap]="wrap" placeholder="Snacks">
|
||||
</textarea>
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputTextareaWithBindings {
|
||||
@@ -1726,3 +1804,58 @@ class AutosizeTextareaInATab {}
|
||||
`
|
||||
})
|
||||
class AutosizeTextareaInAStep {}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<select matNativeControl id="test-id" [disabled]="disabled" [required]="required">
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputSelect {
|
||||
disabled: boolean;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<select matNativeControl>
|
||||
<option value="" disabled selected></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputSelectWithNoLabelNoValue {}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<select matNativeControl>
|
||||
<option value="" label="select a car"></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputSelectWithLabel {}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
<select matNativeControl>
|
||||
<option value="">select a car</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>`
|
||||
})
|
||||
class MatInputSelectWithInnerHtml {}
|
||||
|
||||
+19
-3
@@ -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 `<select>`, the label should float when the selected option has
|
||||
// a non-empty display value. For a `<select multiple>`, 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.
|
||||
|
||||
@@ -8,6 +8,14 @@ To add options to the select, add `<mat-option>` elements to the `<mat-select>`.
|
||||
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 `<mat-option>` is what will be shown to the user.
|
||||
|
||||
Angular Material also supports use of the native `<select>` element inside of
|
||||
`<mat-form-field>`. The native control has several performance, accessibility,
|
||||
and usability advantages. See [the documentation for
|
||||
form-field](https://material.angular.io/components/form-field) for more information.
|
||||
|
||||
To use a native select inside `<mat-form-field>`, add the `matNativeControl` attribute
|
||||
to the `<select>` element.
|
||||
|
||||
<!-- example(select-overview) -->
|
||||
|
||||
### Getting and setting the select value
|
||||
@@ -17,7 +25,7 @@ forms.
|
||||
|
||||
<!-- example(select-value-binding) -->
|
||||
|
||||
The `<mat-select>` also supports all of the form directives from the core `FormsModule` (`NgModel`) and
|
||||
Both`<mat-select>` and `<select>` support all of the form directives from the core `FormsModule` (`NgModel`) and
|
||||
`ReactiveFormsModule` (`FormControl`, `FormGroup`, etc.) As with native `<select>`, `<mat-select>`
|
||||
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 `<mat-form-field>` features that can be used with any `<mat-select>`. These
|
||||
There are a number of `<mat-form-field>` features that can be used with both `<select>` and `<mat-select>`. These
|
||||
include error messages, hint text, prefix & suffix, and theming. For additional information about
|
||||
these features, see the
|
||||
[form field documentation](https://material.angular.io/components/form-field/overview).
|
||||
@@ -45,7 +53,7 @@ In some cases that `<mat-form-field>` may use the placeholder as the label (see
|
||||
### Disabling the select or individual options
|
||||
|
||||
It is possible to disable the entire select or individual options in the select by using the
|
||||
disabled property on the `<mat-select>` and the `<mat-option>` components respectively.
|
||||
disabled property on the `<select>` or `<mat-select>` and the `<option>` or <mat-option>` elements respectively.
|
||||
|
||||
<!-- example(select-disabled) -->
|
||||
|
||||
@@ -71,11 +79,15 @@ by setting the `multiple` property. This will allow the user to select multiple
|
||||
using the `<mat-select>` in multiple selection mode, its value will be a sorted list of all selected
|
||||
values rather than a single value.
|
||||
|
||||
Using multiple selection with a native select element (`<select multiple>`) is discouraged
|
||||
inside `<mat-form-field>`, as the inline listbox appearance is inconsistent with other
|
||||
Material Design components.
|
||||
|
||||
<!-- example(select-multiple) -->
|
||||
|
||||
### Customizing the trigger label
|
||||
|
||||
If you want to display a custom trigger label inside a select, you can use the
|
||||
If you want to display a custom trigger label inside a `<mat-select>`, you can use the
|
||||
`<mat-select-trigger>` element.
|
||||
|
||||
<!-- example(select-custom-trigger) -->
|
||||
@@ -98,7 +110,7 @@ which can be used to apply additional CSS classes to the dropdown panel.
|
||||
|
||||
The `<mat-form-field>` allows you to
|
||||
[associate error messages](https://material.angular.io/components/form-field/overview#error-messages)
|
||||
with your `<mat-select>`. By default, these error messages are shown when the control is invalid and
|
||||
with your `<select>` or `<mat-select>`. By default, these error messages are shown when the control is invalid and
|
||||
either the user has interacted with (touched) the element or the parent form has been submitted. If
|
||||
you wish to override this behavior (e.g. to show the error as soon as the invalid control is dirty
|
||||
or when a parent form group is invalid), you can use the `errorStateMatcher` property of the
|
||||
@@ -130,10 +142,12 @@ globally cause input errors to show when the input is dirty and invalid.
|
||||
|
||||
### Accessibility
|
||||
|
||||
The select component without text or label should be given a meaningful label via
|
||||
The `<mat-select>` component without text or label should be given a meaningful label via
|
||||
`aria-label` or `aria-labelledby`.
|
||||
|
||||
The select component has `role="listbox"` and options inside select have `role="option"`.
|
||||
The `<mat-select>` component has `role="listbox"` and options inside select have `role="option"`.
|
||||
|
||||
The native `<select>` offers the best accessibility because it is supported directly by screen-readers.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
<p>
|
||||
<mat-checkbox [formControl]="disableSelect">Disable select</mat-checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Choose an option" [disabled]="disableSelect.value">
|
||||
<mat-option value="option1">Option 1</mat-option>
|
||||
<mat-option value="option2" disabled>Option 2 (disabled)</mat-option>
|
||||
<mat-option value="option3">Option 3</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
<h4>mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Choose an option" [disabled]="disableSelect.value">
|
||||
<mat-option value="option1">Option 1</mat-option>
|
||||
<mat-option value="option2" disabled>Option 2 (disabled)</mat-option>
|
||||
<mat-option value="option3">Option 3</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field>
|
||||
<select matNativeControl placeholder="Choose an option" [disabled]="disableSelect.value">
|
||||
<option value="" disabled selected></option>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab" disabled>Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
+14
@@ -1,3 +1,4 @@
|
||||
<h4>mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Choose one" [formControl]="selected" [errorStateMatcher]="matcher">
|
||||
<mat-option>Clear</mat-option>
|
||||
@@ -10,3 +11,16 @@
|
||||
Your selection is invalid
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field class="demo-full-width">
|
||||
<select matNativeControl placeholder="Choose one" [formControl]="nativeSelectFormControl" [errorStateMatcher]="matcher">
|
||||
<option value=""></option>
|
||||
<option value="valid" selected>Valid option</option>
|
||||
<option value="invalid">Invalid option</option>
|
||||
</select>
|
||||
<mat-error *ngIf="nativeSelectFormControl.hasError('required')">You must make a selection</mat-error>
|
||||
<mat-error *ngIf="nativeSelectFormControl.hasError('pattern') && !nativeSelectFormControl.hasError('required')">
|
||||
Your selection is invalid
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
+10
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<form>
|
||||
<h4>mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Favorite food" [(ngModel)]="selectedValue" name="food">
|
||||
<mat-option *ngFor="let food of foods" [value]="food.value">
|
||||
@@ -6,6 +7,15 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<p> Selected value: {{selectedValue}} </p>
|
||||
<p> Selected food: {{selectedValue}} </p>
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field>
|
||||
<select matNativeControl placeholder="Favorite car" [(ngModel)]="selectedCar" name="car">
|
||||
<option value="" disabled selected></option>
|
||||
<option *ngFor="let car of cars" [value]="car.value">
|
||||
{{car.viewValue}}
|
||||
</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
<p> Selected car: {{selectedCar}} </p>
|
||||
</form>
|
||||
|
||||
@@ -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'}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<h4>mat select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Favorite animal" [formControl]="animalControl" required>
|
||||
<mat-option>--</mat-option>
|
||||
@@ -8,3 +9,19 @@
|
||||
<mat-error *ngIf="animalControl.hasError('required')">Please choose an animal</mat-error>
|
||||
<mat-hint>{{animalControl.value?.sound}}</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field>
|
||||
<mat-label>Select your car (required)</mat-label>
|
||||
<select matNativeControl required [formControl]="selectFormControl">
|
||||
<option label="--select something --"></option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
<mat-error *ngIf="selectFormControl.hasError('required')">
|
||||
This field is required
|
||||
</mat-error>
|
||||
<mat-hint>You can pick up your favorite car here</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
@@ -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!'},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<h4>mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Pokemon" [formControl]="pokemonControl">
|
||||
<mat-option>-- None --</mat-option>
|
||||
@@ -9,3 +10,17 @@
|
||||
</mat-optgroup>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field>
|
||||
<select matNativeControl>
|
||||
<optgroup label="Swedish Cars">
|
||||
<option value="volvo">volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
</optgroup>
|
||||
<optgroup label="German Cars">
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<h4>Basic mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Favorite food">
|
||||
<mat-option *ngFor="let food of foods" [value]="food.value">
|
||||
@@ -5,3 +6,13 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>Basic native select</h4>
|
||||
<mat-form-field>
|
||||
<select matNativeControl required>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
<h4>mat-select</h4>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="State">
|
||||
<mat-option>None</mat-option>
|
||||
<mat-option *ngFor="let state of states" [value]="state">{{state}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<h4>native html select</h4>
|
||||
<mat-form-field>
|
||||
<mat-label>Select your car</mat-label>
|
||||
<select matNativeControl id="mySelectId">
|
||||
<option value="" disabled selected></option>
|
||||
<option value="volvo">Volvo</option>
|
||||
<option value="saab">Saab</option>
|
||||
<option value="mercedes">Mercedes</option>
|
||||
<option value="audi">Audi</option>
|
||||
</select>
|
||||
</mat-form-field>
|
||||
|
||||
Reference in New Issue
Block a user