fix(select): allow for null values in HTML select options bound with ngValue

closes #12829
This commit is contained in:
Dzmitry Shylovich 2016-11-12 02:58:43 +03:00 committed by Victor Berchet
parent b55aaf094f
commit 46023e4792
3 changed files with 47 additions and 13 deletions

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Provider, Renderer, forwardRef} from '@angular/core';
import {isPrimitive, looseIdentical} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const SELECT_VALUE_ACCESSOR: any = {
export const SELECT_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectControlValueAccessor),
multi: true
@ -115,8 +115,8 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
/** @internal */
_getOptionValue(valueString: string): any {
let key: string = _extractId(valueString);
return this._optionMap.has(key) ? this._optionMap.get(key) : valueString;
const id: string = _extractId(valueString);
return this._optionMap.has(id) ? this._optionMap.get(id) : valueString;
}
}
@ -158,7 +158,7 @@ export class NgSelectOption implements OnDestroy {
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this._select) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Provider, Renderer, forwardRef} from '@angular/core';
import {isPrimitive, looseIdentical} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const SELECT_MULTIPLE_VALUE_ACCESSOR = {
export const SELECT_MULTIPLE_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
multi: true
@ -121,8 +121,8 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
/** @internal */
_getOptionValue(valueString: string): any {
let key: string = _extractId(valueString);
return this._optionMap.has(key) ? this._optionMap.get(key)._value : valueString;
const id: string = _extractId(valueString);
return this._optionMap.has(id) ? this._optionMap.get(id)._value : valueString;
}
}
@ -180,12 +180,10 @@ export class NgSelectMultipleOption implements OnDestroy {
this._renderer.setElementProperty(this._element.nativeElement, 'selected', selected);
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this._select) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);
}
}
}
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];

View File

@ -23,7 +23,7 @@ export function main() {
NgModelRadioForm, NgModelRangeForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
NgModelOptionsStandalone, NgModelCustomComp, NgModelCustomWrapper,
NgModelValidationBindings, NgModelMultipleValidators, NgAsyncValidator,
NgModelAsyncValidation
NgModelAsyncValidation, NgModelSelectWithNullForm
],
imports: [FormsModule]
});
@ -699,6 +699,28 @@ export function main() {
expect(select.nativeElement.value).toEqual('2: Object');
expect(secondNYC.nativeElement.selected).toBe(true);
}));
it('should work with null option', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelSelectWithNullForm);
const comp = fixture.componentInstance;
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}];
comp.selectedCity = null;
fixture.detectChanges();
const select = fixture.debugElement.query(By.css('select'));
select.nativeElement.value = '2: Object';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
tick();
expect(comp.selectedCity['name']).toEqual('NYC');
select.nativeElement.value = '0: null';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
tick();
expect(comp.selectedCity).toEqual(null);
}));
});
describe('custom value accessors', () => {
@ -1078,6 +1100,20 @@ class NgModelSelectForm {
cities: any[] = [];
}
@Component({
selector: 'ng-model-select-null-form',
template: `
<select [(ngModel)]="selectedCity">
<option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
<option [ngValue]="null">Unspecified</option>
</select>
`
})
class NgModelSelectWithNullForm {
selectedCity: {[k: string]: string} = {};
cities: any[] = [];
}
@Component({
selector: 'ng-model-custom-comp',
template: `