fix(forms): support radio buttons with same name but diff parent (#11152)
Closes #10065
This commit is contained in:
parent
d2ad871279
commit
e8a1566065
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import {AbstractControlDirective} from './abstract_control_directive';
|
import {AbstractControlDirective} from './abstract_control_directive';
|
||||||
|
import {ControlContainer} from './control_container';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||||
|
|
||||||
@ -24,6 +25,8 @@ function unimplemented(): any {
|
|||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class NgControl extends AbstractControlDirective {
|
export abstract class NgControl extends AbstractControlDirective {
|
||||||
|
/** @internal */
|
||||||
|
_parent: ControlContainer = null;
|
||||||
name: string = null;
|
name: string = null;
|
||||||
valueAccessor: ControlValueAccessor = null;
|
valueAccessor: ControlValueAccessor = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -71,12 +71,13 @@ export class NgModel extends NgControl implements OnChanges,
|
|||||||
|
|
||||||
@Output('ngModelChange') update = new EventEmitter();
|
@Output('ngModelChange') update = new EventEmitter();
|
||||||
|
|
||||||
constructor(@Optional() @Host() private _parent: ControlContainer,
|
constructor(@Optional() @Host() parent: ControlContainer,
|
||||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
||||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<Validator|AsyncValidatorFn>,
|
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<Validator|AsyncValidatorFn>,
|
||||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||||
valueAccessors: ControlValueAccessor[]) {
|
valueAccessors: ControlValueAccessor[]) {
|
||||||
super();
|
super();
|
||||||
|
this._parent = parent;
|
||||||
this._rawValidators = validators || [];
|
this._rawValidators = validators || [];
|
||||||
this._rawAsyncValidators = asyncValidators || [];
|
this._rawAsyncValidators = asyncValidators || [];
|
||||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||||
|
@ -53,7 +53,7 @@ export class RadioControlRegistry {
|
|||||||
controlPair: [NgControl, RadioControlValueAccessor],
|
controlPair: [NgControl, RadioControlValueAccessor],
|
||||||
accessor: RadioControlValueAccessor): boolean {
|
accessor: RadioControlValueAccessor): boolean {
|
||||||
if (!controlPair[0].control) return false;
|
if (!controlPair[0].control) return false;
|
||||||
return controlPair[0].control.root === accessor._control.control.root &&
|
return controlPair[0]._parent === accessor._control._parent &&
|
||||||
controlPair[1].name === accessor.name;
|
controlPair[1].name === accessor.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,12 +109,13 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||||||
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Optional() @Host() @SkipSelf() private _parent: ControlContainer,
|
@Optional() @Host() @SkipSelf() parent: ControlContainer,
|
||||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
|
||||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
|
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
|
||||||
Array<Validator|AsyncValidatorFn>,
|
Array<Validator|AsyncValidatorFn>,
|
||||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
|
||||||
super();
|
super();
|
||||||
|
this._parent = parent;
|
||||||
this._rawValidators = validators || [];
|
this._rawValidators = validators || [];
|
||||||
this._rawAsyncValidators = asyncValidators || [];
|
this._rawAsyncValidators = asyncValidators || [];
|
||||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||||
|
@ -856,6 +856,49 @@ export function main() {
|
|||||||
expect(form.value).toEqual({drink: 'sprite'});
|
expect(form.value).toEqual({drink: 'sprite'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should differentiate controls on different levels with the same name', () => {
|
||||||
|
TestBed.overrideComponent(FormControlRadioButtons, {
|
||||||
|
set: {
|
||||||
|
template: `
|
||||||
|
<div [formGroup]="form">
|
||||||
|
<input type="radio" formControlName="food" value="chicken">
|
||||||
|
<input type="radio" formControlName="food" value="fish">
|
||||||
|
<div formGroupName="nested">
|
||||||
|
<input type="radio" formControlName="food" value="chicken">
|
||||||
|
<input type="radio" formControlName="food" value="fish">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(FormControlRadioButtons);
|
||||||
|
const form = new FormGroup({
|
||||||
|
food: new FormControl('fish'),
|
||||||
|
nested: new FormGroup({food: new FormControl('fish')})
|
||||||
|
});
|
||||||
|
fixture.debugElement.componentInstance.form = form;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
// model -> view
|
||||||
|
const inputs = fixture.debugElement.queryAll(By.css('input'));
|
||||||
|
expect(inputs[0].nativeElement.checked).toEqual(false);
|
||||||
|
expect(inputs[1].nativeElement.checked).toEqual(true);
|
||||||
|
expect(inputs[2].nativeElement.checked).toEqual(false);
|
||||||
|
expect(inputs[3].nativeElement.checked).toEqual(true);
|
||||||
|
|
||||||
|
dispatchEvent(inputs[0].nativeElement, 'change');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
// view -> model
|
||||||
|
expect(form.get('food').value).toEqual('chicken');
|
||||||
|
expect(form.get('nested.food').value).toEqual('fish');
|
||||||
|
|
||||||
|
expect(inputs[1].nativeElement.checked).toEqual(false);
|
||||||
|
expect(inputs[2].nativeElement.checked).toEqual(false);
|
||||||
|
expect(inputs[3].nativeElement.checked).toEqual(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('custom value accessors', () => {
|
describe('custom value accessors', () => {
|
||||||
|
4
tools/public_api_guard/forms/index.d.ts
vendored
4
tools/public_api_guard/forms/index.d.ts
vendored
@ -245,7 +245,7 @@ export declare class FormControlName extends NgControl implements OnChanges, OnD
|
|||||||
path: string[];
|
path: string[];
|
||||||
update: EventEmitter<{}>;
|
update: EventEmitter<{}>;
|
||||||
validator: ValidatorFn;
|
validator: ValidatorFn;
|
||||||
constructor(_parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
|
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
|
||||||
ngOnChanges(changes: SimpleChanges): void;
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
ngOnDestroy(): void;
|
ngOnDestroy(): void;
|
||||||
viewToModelUpdate(newValue: any): void;
|
viewToModelUpdate(newValue: any): void;
|
||||||
@ -406,7 +406,7 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy {
|
|||||||
update: EventEmitter<{}>;
|
update: EventEmitter<{}>;
|
||||||
validator: ValidatorFn;
|
validator: ValidatorFn;
|
||||||
viewModel: any;
|
viewModel: any;
|
||||||
constructor(_parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
|
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
|
||||||
ngOnChanges(changes: SimpleChanges): void;
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
ngOnDestroy(): void;
|
ngOnDestroy(): void;
|
||||||
viewToModelUpdate(newValue: any): void;
|
viewToModelUpdate(newValue: any): void;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user