[英]Angular: Input radios not maintaining selection in custom component implemented with ControlValueAccessor
我一直在尝试使用 Angular 中的ControlValueAccessor
创建一个自定义无线电组件。
在主组件中,我有一个表单,它有两个表单控件,它们与我创建的自定义无线电组件相关联。 在初始加载中,两个自定义无线电组件都被选中,但是一旦我更改值,只有一个自定义无线电保持选定状态。
似乎有一个问题导致了这种异常行为,我无法弄清楚。 我在实现ControlValueAccessor
的自定义组件中尝试了ngModel
(带有独立选项)和FormControl
,但无济于事。
收音机.html
<div>
<ng-container *ngFor="let o of options; index as i; trackBy: trackByCode">
<input
type="radio"
id="{{ inputName + '_' + o.code }}"
[value]="o.code"
[attr.name]="inputName"
[formControl]="control"
(blur)="onBlur($event)"
(change)="onChange($event)"
/>
<label for="{{ inputName + '_' + o.code }}"> {{ o.name }} </label>
</ng-container>
</div>
radio.component.ts
import { Component, forwardRef, Input } from '@angular/core';
import {
ControlValueAccessor,
FormControl,
NG_VALUE_ACCESSOR,
} from '@angular/forms';
const MY_RADIO_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadioComponent),
multi: true,
};
@Component({
selector: 'radio',
templateUrl: './radio.html',
providers: [MY_RADIO_VALUE_ACCESSOR],
})
export class RadioComponent implements ControlValueAccessor {
@Input() inputName;
@Input() options = [];
control = new FormControl();
private _onChange!: (_: any) => void;
private _onTouched!: (_: any) => void;
trackByCode(index: number, option: any) {
return option.code;
}
writeValue(value: string): void {
this.control.setValue(value);
}
registerOnChange(fn: (_: any) => void): void {
this._onChange = fn;
}
registerOnTouched(fn: (_: any) => void): void {
this._onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
isDisabled ? this.control.disable() : this.control.enable();
}
onChange($event: any) {
this._onChange(this.control.value);
}
onBlur($event: any) {
this._onTouched($event);
}
}
app.component.ts
fruitOptions = [
{ name: 'Tomato', code: 'tomato' },
{ name: 'Apple', code: 'apple' },
{ name: 'Banana', code: 'banana' },
];
vegOptions = [
{ name: 'Cabbage', code: 'cabbage' },
{ name: 'Potato', code: 'potato' },
{ name: 'Beans', code: 'beans' },
];
form = this.fb.group({
fruit: ['tomato', Validators.required],
veg: ['cabbage', Validators.required],
});
app.component.html
<div [formGroup]="form">
<radio inputName="fruit" [options]="fruitOptions" formControlName="fruit"></radio>
<radio inputName="veg" [options]="vegOptions" formControlName="veg"></radio>
</div>
<br />
<div>{{ form.value | json }}</div>
TLDR: Angular 需要[name]
和[attr.name]
最初我有[name]
,但是在 angular 生成的标记中,没有 name 属性。 它正在更改为ng-reflect-name="veg"
并且由于没有name
属性,因此 html 并未将其视为一个组。
<input type="radio" ng-reflect-name="veg" ng-reflect-value="cabbage" ng-reflect-form="[object Object]" id="veg_cabbage" class="ng-valid ng-dirty ng-touched">
后来,我发现我们需要[attr.name]
来告诉 Angular 将其与原生属性区分开来。 这次生成的标记有name
并且看起来不错,但它仍然不起作用。
<input type="radio" ng-reflect-value="cabbage" ng-reflect-form="[object Object]" id="veg_cabbage" name="veg" class="ng-untouched ng-pristine ng-valid">
我尝试了一些其他方法,并通过使用ViewChildren
获取无线电输入列表并通过nativeElement
设置checked
的属性来使其工作。
但是实际的解决方案变得更简单了,在输入上同时设置[name]
和[attr.name]
,一个用于 angular,另一个用于本机 html,终于做到了。
在 radio.html 中不要使用 [attr.name]="inputName" 而是使用 [name]="inputName" 代替。
本质上,现在就像您没有设置 name 属性,所以从 HTML 的角度来看,所有收音机都属于同一个组。
这似乎是一个已知问题: angular github issue #24871
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.