mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(select): support disabling (#1667)
This commit is contained in:
@@ -1,11 +1,30 @@
|
||||
<div class="demo-select">
|
||||
<md-select placeholder="Food" [formControl]="control" [required]="isRequired">
|
||||
<md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option>
|
||||
</md-select>
|
||||
<p> Value: {{ control.value }} </p>
|
||||
<p> Touched: {{ control.touched }} </p>
|
||||
<p> Dirty: {{ control.dirty }} </p>
|
||||
<p> Status: {{ control.status }} </p>
|
||||
<button md-button (click)="control.setValue('pizza-1')">SET VALUE</button>
|
||||
<button md-button (click)="isRequired=!isRequired">TOGGLE REQUIRED</button>
|
||||
<md-card>
|
||||
<md-select placeholder="Food" [formControl]="foodControl">
|
||||
<md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option>
|
||||
</md-select>
|
||||
<p> Value: {{ foodControl.value }} </p>
|
||||
<p> Touched: {{ foodControl.touched }} </p>
|
||||
<p> Dirty: {{ foodControl.dirty }} </p>
|
||||
<p> Status: {{ foodControl.status }} </p>
|
||||
<button md-button (click)="foodControl.setValue('pizza-1')">SET VALUE</button>
|
||||
<button md-button (click)="toggleDisabled()">TOGGLE DISABLED</button>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="isRequired" [disabled]="isDisabled"
|
||||
#drinkControl="ngModel">
|
||||
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
|
||||
{{ drink.viewValue }}
|
||||
</md-option>
|
||||
</md-select>
|
||||
<p> Value: {{ currentDrink }} </p>
|
||||
<p> Touched: {{ drinkControl.touched }} </p>
|
||||
<p> Dirty: {{ drinkControl.dirty }} </p>
|
||||
<p> Status: {{ drinkControl.control?.status }} </p>
|
||||
<button md-button (click)="currentDrink='sprite-1'">SET VALUE</button>
|
||||
<button md-button (click)="isRequired=!isRequired">TOGGLE REQUIRED</button>
|
||||
<button md-button (click)="isDisabled=!isDisabled">TOGGLE DISABLED</button>
|
||||
</md-card>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,2 +1,10 @@
|
||||
.demo-select {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
md-card {
|
||||
width: 450px;
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import {FormControl} from '@angular/forms';
|
||||
})
|
||||
export class SelectDemo {
|
||||
isRequired = false;
|
||||
isDisabled = false;
|
||||
currentDrink: string;
|
||||
foodControl = new FormControl('');
|
||||
|
||||
foods = [
|
||||
{value: 'steak-0', viewValue: 'Steak'},
|
||||
@@ -16,6 +19,14 @@ export class SelectDemo {
|
||||
{value: 'tacos-2', viewValue: 'Tacos'}
|
||||
];
|
||||
|
||||
control = new FormControl('');
|
||||
drinks = [
|
||||
{value: 'coke-0', viewValue: 'Coke'},
|
||||
{value: 'sprite-1', viewValue: 'Sprite', disabled: true},
|
||||
{value: 'water-2', viewValue: 'Water'}
|
||||
];
|
||||
|
||||
toggleDisabled() {
|
||||
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
// Gradient for showing the dashed line when the input is disabled.
|
||||
// Unlike using a border, a gradient allows us to adjust the spacing of the dotted line
|
||||
// to match the Material Design spec.
|
||||
$md-underline-disabled-background-image:
|
||||
linear-gradient(to right, rgba(0, 0, 0, 0.26) 0%, rgba(0, 0, 0, 0.26) 33%, transparent 0%);
|
||||
|
||||
@mixin md-control-disabled-underline {
|
||||
background-image: $md-underline-disabled-background-image;
|
||||
background-size: 4px 1px;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@import '../core/style/variables';
|
||||
@import '../core/style/form-common';
|
||||
|
||||
|
||||
$md-input-floating-placeholder-scale-factor: 0.75 !default;
|
||||
@@ -151,11 +152,9 @@ md-input, md-textarea {
|
||||
border-top-style: solid;
|
||||
|
||||
&.md-disabled {
|
||||
@include md-control-disabled-underline();
|
||||
border-top: 0;
|
||||
background-image: $md-input-underline-disabled-background-image;
|
||||
background-position: 0;
|
||||
background-size: 4px 1px;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.md-input-ripple {
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
color: md-color($foreground, hint-text);
|
||||
border-bottom: 1px solid md-color($foreground, divider);
|
||||
|
||||
md-select:focus & {
|
||||
md-select:focus:not(.md-select-disabled) & {
|
||||
color: md-color($primary);
|
||||
border-bottom: 1px solid md-color($primary);
|
||||
}
|
||||
|
||||
.ng-invalid.ng-touched & {
|
||||
.ng-invalid.ng-touched:not(.md-select-disabled) & {
|
||||
color: md-color($warn);
|
||||
border-bottom: 1px solid md-color($warn);
|
||||
}
|
||||
@@ -25,11 +25,11 @@
|
||||
.md-select-arrow {
|
||||
color: md-color($foreground, hint-text);
|
||||
|
||||
md-select:focus & {
|
||||
md-select:focus:not(.md-select-disabled) & {
|
||||
color: md-color($primary);
|
||||
}
|
||||
|
||||
.ng-invalid.ng-touched & {
|
||||
.ng-invalid.ng-touched:not(.md-select-disabled) & {
|
||||
color: md-color($warn);
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,14 @@
|
||||
|
||||
.md-select-value {
|
||||
color: md-color($foreground, text);
|
||||
|
||||
.md-select-disabled & {
|
||||
color: md-color($foreground, hint-text);
|
||||
}
|
||||
}
|
||||
|
||||
md-option {
|
||||
&:hover, &:focus {
|
||||
&:hover:not(.md-option-disabled), &:focus:not(.md-option-disabled) {
|
||||
background: md-color($background, hover);
|
||||
}
|
||||
|
||||
@@ -52,5 +56,9 @@
|
||||
color: md-color($primary);
|
||||
}
|
||||
|
||||
&.md-option-disabled {
|
||||
color: md-color($foreground, hint-text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<ng-content></ng-content>
|
||||
<div class="md-option-ripple" md-ripple md-ripple-background-color="rgba(0,0,0,0)"
|
||||
<div class="md-option-ripple" *ngIf="!disabled" md-ripple md-ripple-background-color="rgba(0,0,0,0)"
|
||||
[md-ripple-trigger]="_getHostElement()"></div>
|
||||
@@ -8,15 +8,18 @@ import {
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {ENTER, SPACE} from '../core/keyboard/keycodes';
|
||||
import {coerceBooleanProperty} from '../core/coersion/boolean-property';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'md-option',
|
||||
host: {
|
||||
'role': 'option',
|
||||
'tabindex': '0',
|
||||
'[attr.tabindex]': '_getTabIndex()',
|
||||
'[class.md-selected]': 'selected',
|
||||
'[attr.aria-selected]': 'selected.toString()',
|
||||
'[attr.aria-disabled]': 'disabled.toString()',
|
||||
'[class.md-option-disabled]': 'disabled',
|
||||
'(click)': '_selectViaInteraction()',
|
||||
'(keydown)': '_handleKeydown($event)'
|
||||
},
|
||||
@@ -25,11 +28,23 @@ import {ENTER, SPACE} from '../core/keyboard/keycodes';
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class MdOption {
|
||||
private _selected = false;
|
||||
private _selected: boolean = false;
|
||||
|
||||
/** Whether the option is disabled. */
|
||||
private _disabled: boolean = false;
|
||||
|
||||
/** The form value of the option. */
|
||||
@Input() value: any;
|
||||
|
||||
@Input()
|
||||
get disabled() {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(value: any) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
/** Event emitted when the option is selected. */
|
||||
@Output() onSelect = new EventEmitter();
|
||||
|
||||
@@ -72,13 +87,21 @@ export class MdOption {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selects the option while indicating the selection came from the user. Used to
|
||||
* determine if the select's view -> model callback should be invoked.
|
||||
*/
|
||||
_selectViaInteraction() {
|
||||
this._selected = true;
|
||||
this.onSelect.emit(true);
|
||||
if (!this.disabled) {
|
||||
this._selected = true;
|
||||
this.onSelect.emit(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the correct tabindex for the option depending on disabled state. */
|
||||
_getTabIndex() {
|
||||
return this.disabled ? '-1' : '0';
|
||||
}
|
||||
|
||||
_getHostElement(): HTMLElement {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import '../core/style/menu-common';
|
||||
@import '../core/style/form-common';
|
||||
|
||||
$md-select-trigger-height: 30px !default;
|
||||
$md-select-trigger-min-width: 112px !default;
|
||||
@@ -16,6 +17,14 @@ md-select {
|
||||
height: $md-select-trigger-height;
|
||||
min-width: $md-select-trigger-min-width;
|
||||
cursor: pointer;
|
||||
|
||||
[aria-disabled='true'] & {
|
||||
@include md-control-disabled-underline();
|
||||
border-bottom: transparent;
|
||||
background-position: 0 bottom;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.md-select-placeholder {
|
||||
@@ -56,6 +65,11 @@ md-option {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&[aria-disabled='true'] {
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.md-option-ripple {
|
||||
|
||||
+163
-14
@@ -6,7 +6,7 @@ import {OverlayContainer} from '../core/overlay/overlay-container';
|
||||
import {MdSelect} from './select';
|
||||
import {MdOption} from './option';
|
||||
import {Dir} from '../core/rtl/dir';
|
||||
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
describe('MdSelect', () => {
|
||||
let overlayContainerElement: HTMLElement;
|
||||
@@ -14,8 +14,8 @@ describe('MdSelect', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MdSelectModule.forRoot(), ReactiveFormsModule],
|
||||
declarations: [BasicSelect],
|
||||
imports: [MdSelectModule.forRoot(), ReactiveFormsModule, FormsModule],
|
||||
declarations: [BasicSelect, NgModelSelect],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
overlayContainerElement = document.createElement('div');
|
||||
@@ -205,6 +205,20 @@ describe('MdSelect', () => {
|
||||
.toBe(fixture.componentInstance.options.last);
|
||||
});
|
||||
|
||||
it('should not select disabled options', () => {
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const options =
|
||||
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
|
||||
options[2].click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.select.panelOpen).toBe(true);
|
||||
expect(options[2].classList).not.toContain('md-selected');
|
||||
expect(fixture.componentInstance.select.selected).not.toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('forms integration', () => {
|
||||
@@ -315,6 +329,84 @@ describe('MdSelect', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('disabled behavior', () => {
|
||||
|
||||
it('should disable itself when control is disabled programmatically', () => {
|
||||
const fixture = TestBed.createComponent(BasicSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.control.disable();
|
||||
fixture.detectChanges();
|
||||
let trigger =
|
||||
fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
|
||||
expect(getComputedStyle(trigger).getPropertyValue('cursor'))
|
||||
.toEqual('default', `Expected cursor to be default arrow on disabled control.`);
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toEqual('', `Expected select panel to stay closed.`);
|
||||
expect(fixture.componentInstance.select.panelOpen)
|
||||
.toBe(false, `Expected select panelOpen property to stay false.`);
|
||||
|
||||
fixture.componentInstance.control.enable();
|
||||
fixture.detectChanges();
|
||||
expect(getComputedStyle(trigger).getPropertyValue('cursor'))
|
||||
.toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Steak', `Expected select panel to open normally on re-enabled control`);
|
||||
expect(fixture.componentInstance.select.panelOpen)
|
||||
.toBe(true, `Expected select panelOpen property to become true.`);
|
||||
});
|
||||
|
||||
it('should disable itself when control is disabled using the property', async(() => {
|
||||
const fixture = TestBed.createComponent(NgModelSelect);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.isDisabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let trigger =
|
||||
fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
|
||||
expect(getComputedStyle(trigger).getPropertyValue('cursor'))
|
||||
.toEqual('default', `Expected cursor to be default arrow on disabled control.`);
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toEqual('', `Expected select panel to stay closed.`);
|
||||
expect(fixture.componentInstance.select.panelOpen)
|
||||
.toBe(false, `Expected select panelOpen property to stay false.`);
|
||||
|
||||
fixture.componentInstance.isDisabled = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(getComputedStyle(trigger).getPropertyValue('cursor'))
|
||||
.toEqual('pointer', `Expected cursor to be a pointer on enabled control.`);
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Steak', `Expected select panel to open normally on re-enabled control`);
|
||||
expect(fixture.componentInstance.select.panelOpen)
|
||||
.toBe(true, `Expected select panelOpen property to become true.`);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('animations', () => {
|
||||
let fixture: ComponentFixture<BasicSelect>;
|
||||
let trigger: HTMLElement;
|
||||
@@ -427,25 +519,48 @@ describe('MdSelect', () => {
|
||||
.toEqual('true', `Expected aria-invalid attr to be true for invalid selects.`);
|
||||
});
|
||||
|
||||
it('should set aria-disabled for disabled selects', () => {
|
||||
expect(select.getAttribute('aria-disabled')).toEqual('false');
|
||||
|
||||
fixture.componentInstance.control.disable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(select.getAttribute('aria-disabled')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should set the tabindex of the select to -1 if disabled', () => {
|
||||
fixture.componentInstance.control.disable();
|
||||
fixture.detectChanges();
|
||||
expect(select.getAttribute('tabindex')).toEqual('-1');
|
||||
|
||||
fixture.componentInstance.control.enable();
|
||||
fixture.detectChanges();
|
||||
expect(select.getAttribute('tabindex')).toEqual('0');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('for options', () => {
|
||||
let trigger: HTMLElement;
|
||||
let options: NodeListOf<HTMLElement>;
|
||||
|
||||
beforeEach(() => {
|
||||
trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
options =
|
||||
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
|
||||
});
|
||||
|
||||
it('should set the role of md-option to option', () => {
|
||||
const option = overlayContainerElement.querySelector('md-option') as HTMLElement;
|
||||
expect(option.getAttribute('role')).toEqual('option');
|
||||
expect(options[0].getAttribute('role')).toEqual('option');
|
||||
expect(options[1].getAttribute('role')).toEqual('option');
|
||||
expect(options[2].getAttribute('role')).toEqual('option');
|
||||
});
|
||||
|
||||
it('should set aria-selected on each option', () => {
|
||||
const options =
|
||||
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
|
||||
expect(options[0].getAttribute('aria-selected')).toEqual('false');
|
||||
expect(options[1].getAttribute('aria-selected')).toEqual('false');
|
||||
expect(options[2].getAttribute('aria-selected')).toEqual('false');
|
||||
@@ -461,12 +576,23 @@ describe('MdSelect', () => {
|
||||
expect(options[2].getAttribute('aria-selected')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should set the tabindex of each option to 0', () => {
|
||||
const options =
|
||||
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
|
||||
it('should set the tabindex of each option according to disabled state', () => {
|
||||
expect(options[0].getAttribute('tabindex')).toEqual('0');
|
||||
expect(options[1].getAttribute('tabindex')).toEqual('0');
|
||||
expect(options[2].getAttribute('tabindex')).toEqual('0');
|
||||
expect(options[2].getAttribute('tabindex')).toEqual('-1');
|
||||
});
|
||||
|
||||
it('should set aria-disabled for disabled options', () => {
|
||||
expect(options[0].getAttribute('aria-disabled')).toEqual('false');
|
||||
expect(options[1].getAttribute('aria-disabled')).toEqual('false');
|
||||
expect(options[2].getAttribute('aria-disabled')).toEqual('true');
|
||||
|
||||
fixture.componentInstance.foods[2]['disabled'] = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(options[0].getAttribute('aria-disabled')).toEqual('false');
|
||||
expect(options[1].getAttribute('aria-disabled')).toEqual('false');
|
||||
expect(options[2].getAttribute('aria-disabled')).toEqual('false');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -479,15 +605,17 @@ describe('MdSelect', () => {
|
||||
selector: 'basic-select',
|
||||
template: `
|
||||
<md-select placeholder="Food" [formControl]="control" [required]="isRequired">
|
||||
<md-option *ngFor="let food of foods" [value]="food.value">{{ food.viewValue }}</md-option>
|
||||
<md-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
|
||||
{{ food.viewValue }}
|
||||
</md-option>
|
||||
</md-select>
|
||||
`
|
||||
})
|
||||
class BasicSelect {
|
||||
foods = [
|
||||
foods: any[] = [
|
||||
{ value: 'steak-0', viewValue: 'Steak' },
|
||||
{ value: 'pizza-1', viewValue: 'Pizza' },
|
||||
{ value: 'tacos-2', viewValue: 'Tacos' },
|
||||
{ value: 'tacos-2', viewValue: 'Tacos', disabled: true },
|
||||
];
|
||||
control = new FormControl();
|
||||
isRequired: boolean;
|
||||
@@ -496,6 +624,27 @@ class BasicSelect {
|
||||
@ViewChildren(MdOption) options: QueryList<MdOption>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ng-model-select',
|
||||
template: `
|
||||
<md-select placeholder="Food" ngModel [disabled]="isDisabled">
|
||||
<md-option *ngFor="let food of foods" [value]="food.value">{{ food.viewValue }}</md-option>
|
||||
</md-select>
|
||||
`
|
||||
})
|
||||
class NgModelSelect {
|
||||
foods: any[] = [
|
||||
{ value: 'steak-0', viewValue: 'Steak' },
|
||||
{ value: 'pizza-1', viewValue: 'Pizza' },
|
||||
{ value: 'tacos-2', viewValue: 'Tacos' },
|
||||
];
|
||||
isDisabled: boolean;
|
||||
|
||||
@ViewChild(MdSelect) select: MdSelect;
|
||||
@ViewChildren(MdOption) options: QueryList<MdOption>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Move this to core testing utility until Angular has event faking
|
||||
* support.
|
||||
|
||||
@@ -30,9 +30,11 @@ import {coerceBooleanProperty} from '../core/coersion/boolean-property';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: {
|
||||
'role': 'listbox',
|
||||
'tabindex': '0',
|
||||
'[attr.tabindex]': '_getTabIndex()',
|
||||
'[attr.aria-label]': 'placeholder',
|
||||
'[attr.aria-required]': 'required.toString()',
|
||||
'[attr.aria-disabled]': 'disabled.toString()',
|
||||
'[class.md-select-disabled]': 'disabled',
|
||||
'[attr.aria-invalid]': '_control?.invalid || "false"',
|
||||
'(keydown)': '_handleKeydown($event)',
|
||||
'(blur)': '_onBlur()'
|
||||
@@ -63,6 +65,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
/** Whether filling out the select is required in the form. */
|
||||
private _required: boolean = false;
|
||||
|
||||
/** Whether the select is disabled. */
|
||||
private _disabled: boolean = false;
|
||||
|
||||
/** Manages keyboard events for options in the panel. */
|
||||
_keyManager: ListKeyManager;
|
||||
|
||||
@@ -88,6 +93,15 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
|
||||
@Input() placeholder: string;
|
||||
|
||||
@Input()
|
||||
get disabled() {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(value: any) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
get required() {
|
||||
return this._required;
|
||||
@@ -128,6 +142,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
|
||||
/** Opens the overlay panel. */
|
||||
open(): void {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
this._panelOpen = true;
|
||||
}
|
||||
|
||||
@@ -169,6 +186,14 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
this._onTouched = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the select. Part of the ControlValueAccessor interface required
|
||||
* to integrate with Angular's core forms API.
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
/** Whether or not the overlay panel is open. */
|
||||
get panelOpen(): boolean {
|
||||
return this._panelOpen;
|
||||
@@ -234,6 +259,11 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the correct tabindex for the select depending on disabled state. */
|
||||
_getTabIndex() {
|
||||
return this.disabled ? '-1' : '0';
|
||||
}
|
||||
|
||||
/** Sets up a key manager to listen to keyboard events on the overlay panel. */
|
||||
private _initKeyManager() {
|
||||
this._keyManager = new ListKeyManager(this.options);
|
||||
|
||||
Reference in New Issue
Block a user