Skip to content

Commit

Permalink
fix(select): allow option with undefined or null value to clear selec…
Browse files Browse the repository at this point in the history
…tion

Allows for options, with a value of `null` or `undefined`, to clear the select. This is similar to the way the native select works.

Fixes #3110.
Fixes #2634.
  • Loading branch information
crisbeto committed Feb 24, 2017
1 parent c203589 commit d6fe305
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/demo-app/select/select-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<md-card>
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="isRequired" [disabled]="isDisabled"
[floatPlaceholder]="floatPlaceholder" #drinkControl="ngModel">
#drinkControl="ngModel">
<md-option>None</md-option>
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
{{ drink.viewValue }}
</md-option>
Expand Down
1 change: 1 addition & 0 deletions src/demo-app/select/select-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class SelectDemo {
foodControl = new FormControl('pizza-1');

foods = [
{value: null, viewValue: 'None'},
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos'}
Expand Down
95 changes: 94 additions & 1 deletion src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ describe('MdSelect', () => {
FloatPlaceholderSelect,
SelectWithErrorSibling,
ThrowsErrorOnInit,
BasicSelectOnPush
BasicSelectOnPush,
ResetValuesSelect
],
providers: [
{provide: OverlayContainer, useFactory: () => {
Expand Down Expand Up @@ -1342,6 +1343,72 @@ describe('MdSelect', () => {
expect(trigger.textContent).not.toContain('Pizza');
});
});

describe('reset values', () => {
let fixture: ComponentFixture<ResetValuesSelect>;
let trigger: HTMLElement;
let placeholder: HTMLElement;
let options: NodeListOf<HTMLElement>;


beforeEach(() => {
fixture = TestBed.createComponent(ResetValuesSelect);
fixture.detectChanges();
trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
placeholder = fixture.debugElement.query(By.css('.mat-select-placeholder')).nativeElement;

trigger.click();
fixture.detectChanges();
options = overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;

options[0].click();
fixture.detectChanges();
});

it('should reset when an option with an undefined value is selected', () => {
options[4].click();
fixture.detectChanges();

expect(fixture.componentInstance.control.value).toBeUndefined();
expect(fixture.componentInstance.select.selected).toBeFalsy();
expect(placeholder.classList).not.toContain('mat-floating-placeholder');
expect(trigger.textContent).not.toContain('Undefined');
});

it('should reset when an option with a null value is selected', () => {
options[5].click();
fixture.detectChanges();

expect(fixture.componentInstance.control.value).toBeNull();
expect(fixture.componentInstance.select.selected).toBeFalsy();
expect(placeholder.classList).not.toContain('mat-floating-placeholder');
expect(trigger.textContent).not.toContain('Null');
});

it('should not reset when any other falsy option is selected', () => {
options[3].click();
fixture.detectChanges();

expect(fixture.componentInstance.control.value).toBe(false);
expect(fixture.componentInstance.select.selected).toBeTruthy();
expect(placeholder.classList).toContain('mat-floating-placeholder');
expect(trigger.textContent).toContain('Falsy');
});

it('should not consider the reset values as selected when resetting the form control', () => {
expect(placeholder.classList).toContain('mat-floating-placeholder');

fixture.componentInstance.control.reset();
fixture.detectChanges();

expect(fixture.componentInstance.control.value).toBeNull();
expect(fixture.componentInstance.select.selected).toBeFalsy();
expect(placeholder.classList).not.toContain('mat-floating-placeholder');
expect(trigger.textContent).not.toContain('Null');
expect(trigger.textContent).not.toContain('Undefined');
});

});
});


Expand Down Expand Up @@ -1588,6 +1655,32 @@ class FloatPlaceholderSelect {
@ViewChild(MdSelect) select: MdSelect;
}

@Component({
selector: 'reset-values-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-select>
`
})
class ResetValuesSelect {
foods: any[] = [
{ value: 'steak-0', viewValue: 'Steak' },
{ value: 'pizza-1', viewValue: 'Pizza' },
{ value: 'tacos-2', viewValue: 'Tacos' },
{ value: false, viewValue: 'Falsy' },
{ viewValue: 'Undefined' },
{ value: null, viewValue: 'Null' },
];
control = new FormControl();
isRequired: boolean;

@ViewChild(MdSelect) select: MdSelect;
@ViewChildren(MdOption) options: QueryList<MdOption>;
}


/**
* TODO: Move this to core testing utility until Angular has event faking
Expand Down
2 changes: 1 addition & 1 deletion src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr

/** When a new option is selected, deselects the others and closes the panel. */
private _onSelect(option: MdOption): void {
this._selected = option;
this._selected = option.value == null ? null : option;
this._updateOptions();
this._setValueWidth();
this._placeholderState = '';
Expand Down

0 comments on commit d6fe305

Please sign in to comment.