fix(forms): re-assigning options should not clear select

Fixes #18330
This commit is contained in:
Kara Erickson 2017-08-14 14:33:54 -07:00 committed by Hans
parent c65f18a2fa
commit 32ff21c16b
2 changed files with 59 additions and 3 deletions

View File

@ -129,8 +129,8 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
registerOnChange(fn: (value: any) => any): void { registerOnChange(fn: (value: any) => any): void {
this.onChange = (valueString: string) => { this.onChange = (valueString: string) => {
this.value = valueString; this.value = this._getOptionValue(valueString);
fn(this._getOptionValue(valueString)); fn(this.value);
}; };
} }
registerOnTouched(fn: () => any): void { this.onTouched = fn; } registerOnTouched(fn: () => any): void { this.onTouched = fn; }

View File

@ -8,7 +8,7 @@
import {Component, Directive, EventEmitter, Input, Output, Type} from '@angular/core'; import {Component, Directive, EventEmitter, Input, Output, Type} from '@angular/core';
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing'; import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, ReactiveFormsModule, Validators} from '@angular/forms'; import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, NgModel, ReactiveFormsModule, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by'; import {By} from '@angular/platform-browser/src/dom/debug/by';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
@ -210,6 +210,31 @@ export function main() {
expect(sfOption.nativeElement.selected).toBe(true); expect(sfOption.nativeElement.selected).toBe(true);
}); });
it('should support re-assigning the options array with compareWith', () => {
const fixture = initTest(FormControlSelectWithCompareFn);
fixture.detectChanges();
// Option IDs start out as 0 and 1, so setting the select value to "1: Object"
// will select the second option (NY).
const select = fixture.debugElement.query(By.css('select'));
select.nativeElement.value = '1: Object';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
expect(fixture.componentInstance.form.value).toEqual({city: {id: 2, name: 'NY'}});
fixture.componentInstance.cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}];
fixture.detectChanges();
// Now that the options array has been re-assigned, new option instances will
// be created by ngFor. These instances will have different option IDs, subsequent
// to the first: 2 and 3. For the second option to stay selected, the select
// value will need to have the ID of the current second option: 3.
const nyOption = fixture.debugElement.queryAll(By.css('option'))[1];
expect(select.nativeElement.value).toEqual('3: Object');
expect(nyOption.nativeElement.selected).toBe(true);
});
}); });
describe('in template-driven forms', () => { describe('in template-driven forms', () => {
@ -335,6 +360,37 @@ export function main() {
expect(sfOption.nativeElement.selected).toBe(true); expect(sfOption.nativeElement.selected).toBe(true);
})); }));
it('should support re-assigning the options array with compareWith', fakeAsync(() => {
const fixture = initTest(NgModelSelectWithCustomCompareFnForm);
fixture.componentInstance.selectedCity = {id: 1, name: 'SF'};
fixture.componentInstance.cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}];
fixture.detectChanges();
tick();
// Option IDs start out as 0 and 1, so setting the select value to "1: Object"
// will select the second option (NY).
const select = fixture.debugElement.query(By.css('select'));
select.nativeElement.value = '1: Object';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
const model = fixture.debugElement.children[0].injector.get(NgModel);
expect(model.value).toEqual({id: 2, name: 'NY'});
fixture.componentInstance.cities = [{id: 1, name: 'SF'}, {id: 2, name: 'NY'}];
fixture.detectChanges();
tick();
// Now that the options array has been re-assigned, new option instances will
// be created by ngFor. These instances will have different option IDs, subsequent
// to the first: 2 and 3. For the second option to stay selected, the select
// value will need to have the ID of the current second option: 3.
const nyOption = fixture.debugElement.queryAll(By.css('option'))[1];
expect(select.nativeElement.value).toEqual('3: Object');
expect(nyOption.nativeElement.selected).toBe(true);
}));
}); });
}); });