RadioButtonGroup-formControlName 不适用于反应形式
RadioButtonGroup-formControlName not working with reactive forms
我想创建一个 RadioComponent 来填充输入和标签。
我希望此组件与 ngModel 和响应式表单一起使用。
所以我通过实现 ControlValueAccessor 和填充输入和标签的 RadioButton 组件创建了一个 RadioGroup。
我正在从我定义 FormGroup 的父组件调用这些组件,但反应式表单不适用于此方法。
谁能告诉我哪里出错了。
https://stackblitz.com/edit/angular-mipzzw
RadioGroupComponent
import { Component, Input, forwardRef, ContentChildren, QueryList } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
import { RadioButtonComponent } from '../radio-button/radio-button.component';
@Component({
selector: 'radio-group',
templateUrl: './radio-group.component.html',
styleUrls: ['./radio-group.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadioGroupComponent),
multi: true
}
],
})
export class RadioGroupComponent implements ControlValueAccessor {
constructor() { }
private _value: any = null;
@ContentChildren(forwardRef(() => RadioButtonComponent), { descendants: true })
radios: QueryList<RadioButtonComponent>;
_onChange = (value: any) => {
console.log('onChange=', value);
}
_onTouched = () => { };
writeValue(value: any): void {
console.log('writeValue=', value);
this._onChange(value);
}
registerOnChange(fn: (_: any) => {}): void {
this._onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this._onTouched = fn;
}
get value(): any {
console.log('get value', this._value);
return this._value;
}
set value(val) { // this value is updated by programmatic changes
if (val !== undefined && this._value !== val) {
this._value = val;
this._onChange(val);
this._onTouched();
}
}
}
无线电组件
import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
@Component({
selector: 'radio-button',
template: `
<input type="radio"
(change)="changeValue(value)"
class="input-control"
[id]="inputId"
[(ngModel)]="selectedOption"
[value]="value"
[formControlName]="inputName"/>
<label
for="{{ inputId }}">
<span>{{ label | translate }}</span>
</label>
`,
styleUrls: ['./radio.less']
})
export class RadioComponent {
@Input() selectedOption: string;
@Input() inputId: string;
@Input() value: string;
@Input() label: string;
@Input() inputName: string;
@Output() selectedOptionChange: EventEmitter<any> = new EventEmitter<any>();
changeValue(value: any) {
this.selectedOptionChange.emit(value);
}
}
演示组件
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-radio',
template: `
<div [formGroup]="exampleForm">
<radio-button *ngFor="let option of options"
[inputId] = "option.value"
[label]="option.display"
[value]="option.value"
(selectedOptionChange) = "selectedOptionChange($event)"
formInputName= "gender"
[selectedOption]="options[0].value">
</radio-button>
</div>
</section>
<h3>Selected value = {{selectedOption}} </h3>
`,
})
export class DemoComponent implements OnInit {
constructor(private fb: FormBuilder) { }
exampleForm = this.fb.group({
gender: ['', []],
});
selectedOption: string;
options: Array<{ value: string, display: string }> = [
{ value: 'Male', display: 'Male' },
{ value: 'Female', display: 'Female' }
];
ngOnInit() {
this.selectedOptionChange(this.options[0].value);
}
selectedOptionChange(value: any): void {
this.selectedOption = value;
}
}
您可能还需要请求您的 formGroup 作为输入
@Input() group: FormGroup;
并且您还需要一个元素作为容器,您可以在其中添加指令
[formGroup]
检查我对你的代码所做的编辑
import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
@Component({
selector: 'radio-button',
//You will also need a DOM element to act as a container of your formControl
template: `
<div [formGroup]="group">
<input type="radio"
(change)="changeValue(value)"
class="input-control"
[id]="inputId"
[(ngModel)]="selectedOption"
[value]="value"
[formControlName]="inputName"/>
<label
for="{{ inputId }}">
<span>{{ label | translate }}</span>
</label>
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadioComponent),
multi: true
}
],
styleUrls: ['./radio.less']
})
export class RadioComponent implements ControlValueAccessor {
@Input() selectedOption: string;
@Input() inputId: string;
@Input() value: string;
@Input() label: string;
@Input() inputName: string;
@Input() group: FormGroup; //You also need to ask for the formgroup
@Output() selectedOptionChange: EventEmitter<any> = new EventEmitter<any>();
changeValue(value: any) {
this.selectedOptionChange.emit(value);
}
_onChange = (_: any) => { };
_onTouched = () => { };
writeValue(value: any): void {
console.log('writeValue=', value);
this._onChange(value);
}
registerOnChange(fn: (_: any) => {}): void {
this._onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this._onTouched = fn;
}
}
您还可以 post 一个工作示例,以便我们更好地帮助您。也许上传到 https://stackblitz.com
编辑
为了完整起见,我还添加了您的演示组件
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-radio',
template: `
<div [formGroup]="exampleForm">
<radio-button *ngFor="let option of options"
[inputId] = "option.value"
[label]="option.display"
[value]="option.value"
(selectedOptionChange) = "selectedOptionChange($event)"
formInputName= "gender"
[selectedOption]="options[0].value"
[group]="exampleForm"
>
</radio-button>
</div>
</section>
<h3>Selected value = {{selectedOption}} </h3>
`,
})
export class DemoComponent implements OnInit {
constructor(private fb: FormBuilder) { }
exampleForm = this.fb.group({
gender: ['', []],
});
selectedOption: string;
options: Array<{ value: string, display: string }> = [
{ value: 'Male', display: 'Male' },
{ value: 'Female', display: 'Female' }
];
ngOnInit() {
this.selectedOptionChange(this.options[0].value);
}
selectedOptionChange(value: any): void {
this.selectedOption = value;
}
}
编辑:概念证明
您可以查看概念验证 here。这是您正在做的事情的准系统版本。它只有 3 个组件。 app-component,还有两个跟你做的类似。
我想创建一个 RadioComponent 来填充输入和标签。
我希望此组件与 ngModel 和响应式表单一起使用。
所以我通过实现 ControlValueAccessor 和填充输入和标签的 RadioButton 组件创建了一个 RadioGroup。
我正在从我定义 FormGroup 的父组件调用这些组件,但反应式表单不适用于此方法。
谁能告诉我哪里出错了。
https://stackblitz.com/edit/angular-mipzzw
RadioGroupComponent
import { Component, Input, forwardRef, ContentChildren, QueryList } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
import { RadioButtonComponent } from '../radio-button/radio-button.component';
@Component({
selector: 'radio-group',
templateUrl: './radio-group.component.html',
styleUrls: ['./radio-group.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadioGroupComponent),
multi: true
}
],
})
export class RadioGroupComponent implements ControlValueAccessor {
constructor() { }
private _value: any = null;
@ContentChildren(forwardRef(() => RadioButtonComponent), { descendants: true })
radios: QueryList<RadioButtonComponent>;
_onChange = (value: any) => {
console.log('onChange=', value);
}
_onTouched = () => { };
writeValue(value: any): void {
console.log('writeValue=', value);
this._onChange(value);
}
registerOnChange(fn: (_: any) => {}): void {
this._onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this._onTouched = fn;
}
get value(): any {
console.log('get value', this._value);
return this._value;
}
set value(val) { // this value is updated by programmatic changes
if (val !== undefined && this._value !== val) {
this._value = val;
this._onChange(val);
this._onTouched();
}
}
}
无线电组件
import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
@Component({
selector: 'radio-button',
template: `
<input type="radio"
(change)="changeValue(value)"
class="input-control"
[id]="inputId"
[(ngModel)]="selectedOption"
[value]="value"
[formControlName]="inputName"/>
<label
for="{{ inputId }}">
<span>{{ label | translate }}</span>
</label>
`,
styleUrls: ['./radio.less']
})
export class RadioComponent {
@Input() selectedOption: string;
@Input() inputId: string;
@Input() value: string;
@Input() label: string;
@Input() inputName: string;
@Output() selectedOptionChange: EventEmitter<any> = new EventEmitter<any>();
changeValue(value: any) {
this.selectedOptionChange.emit(value);
}
}
演示组件
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-radio',
template: `
<div [formGroup]="exampleForm">
<radio-button *ngFor="let option of options"
[inputId] = "option.value"
[label]="option.display"
[value]="option.value"
(selectedOptionChange) = "selectedOptionChange($event)"
formInputName= "gender"
[selectedOption]="options[0].value">
</radio-button>
</div>
</section>
<h3>Selected value = {{selectedOption}} </h3>
`,
})
export class DemoComponent implements OnInit {
constructor(private fb: FormBuilder) { }
exampleForm = this.fb.group({
gender: ['', []],
});
selectedOption: string;
options: Array<{ value: string, display: string }> = [
{ value: 'Male', display: 'Male' },
{ value: 'Female', display: 'Female' }
];
ngOnInit() {
this.selectedOptionChange(this.options[0].value);
}
selectedOptionChange(value: any): void {
this.selectedOption = value;
}
}
您可能还需要请求您的 formGroup 作为输入
@Input() group: FormGroup;
并且您还需要一个元素作为容器,您可以在其中添加指令
[formGroup]
检查我对你的代码所做的编辑
import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup } from '@angular/forms';
@Component({
selector: 'radio-button',
//You will also need a DOM element to act as a container of your formControl
template: `
<div [formGroup]="group">
<input type="radio"
(change)="changeValue(value)"
class="input-control"
[id]="inputId"
[(ngModel)]="selectedOption"
[value]="value"
[formControlName]="inputName"/>
<label
for="{{ inputId }}">
<span>{{ label | translate }}</span>
</label>
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadioComponent),
multi: true
}
],
styleUrls: ['./radio.less']
})
export class RadioComponent implements ControlValueAccessor {
@Input() selectedOption: string;
@Input() inputId: string;
@Input() value: string;
@Input() label: string;
@Input() inputName: string;
@Input() group: FormGroup; //You also need to ask for the formgroup
@Output() selectedOptionChange: EventEmitter<any> = new EventEmitter<any>();
changeValue(value: any) {
this.selectedOptionChange.emit(value);
}
_onChange = (_: any) => { };
_onTouched = () => { };
writeValue(value: any): void {
console.log('writeValue=', value);
this._onChange(value);
}
registerOnChange(fn: (_: any) => {}): void {
this._onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this._onTouched = fn;
}
}
您还可以 post 一个工作示例,以便我们更好地帮助您。也许上传到 https://stackblitz.com
编辑
为了完整起见,我还添加了您的演示组件
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-radio',
template: `
<div [formGroup]="exampleForm">
<radio-button *ngFor="let option of options"
[inputId] = "option.value"
[label]="option.display"
[value]="option.value"
(selectedOptionChange) = "selectedOptionChange($event)"
formInputName= "gender"
[selectedOption]="options[0].value"
[group]="exampleForm"
>
</radio-button>
</div>
</section>
<h3>Selected value = {{selectedOption}} </h3>
`,
})
export class DemoComponent implements OnInit {
constructor(private fb: FormBuilder) { }
exampleForm = this.fb.group({
gender: ['', []],
});
selectedOption: string;
options: Array<{ value: string, display: string }> = [
{ value: 'Male', display: 'Male' },
{ value: 'Female', display: 'Female' }
];
ngOnInit() {
this.selectedOptionChange(this.options[0].value);
}
selectedOptionChange(value: any): void {
this.selectedOption = value;
}
}
编辑:概念证明
您可以查看概念验证 here。这是您正在做的事情的准系统版本。它只有 3 个组件。 app-component,还有两个跟你做的类似。