【问题标题】:RadioButtonGroup-formControlName not working with reactive formsRadioButtonGroup-formControlName 不适用于反应式表单
【发布时间】:2019-11-25 17:00:32
【问题描述】:

我想创建一个 RadioComponent 来填充输入和标签。

我希望这个组件能够与 ngModel 和响应式表单一起使用。

所以我通过实现 ControlValueAccessor 和 RadioButton 组件创建了一个 RadioGroup,用于填充 Input 和 Label。

我从定义 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;
    }
}

【问题讨论】:

    标签: angular angular-material angular-reactive-forms angular8


    【解决方案1】:

    您可能还需要请求您的 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;
      }
    }
    

    您还可以发布一个工作示例,以便我们更好地帮助您。可以上传到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 个组件。应用程序组件,还有两个与您正在做的类似。

    【讨论】:

    • 将 formGroup 添加到 RadioComponent 将创建带有 formgroup 的多个元素,这意味着我可以检查“男性”和“女性”这两个选项。唯一的方法是我将 ngFor 移动到我试图避免的无线电组件内。
    • @Vin05 我不确定,但我使用相同的模式在选项卡组件之间拆分表单,我使用单个 formGroup 作为我的 formGroup 的引用传递给孩子成分。艰难我不使用 UseExisting 位
    • @Vino5 如果您在谈论 DIV,那么您可以使用 NG-CONTAINER,这样就不会添加新的 DOM 元素。见参考here
    • 你好@Omar 我把我的例子上传到stackBlitz 你能帮忙看看吗? stackblitz.com/edit/angular-mipzzw 我将 formControlName 行注释为它不显示任何内容。
    • @Vin05 检查我对fork of your app 所做的更改。我评论了我添加或删除内容的地方
    猜你喜欢
    • 2021-11-08
    • 2020-04-19
    • 2019-02-19
    • 2021-09-21
    • 2019-11-14
    • 1970-01-01
    • 2019-05-20
    • 2019-03-24
    • 2018-02-18
    相关资源
    最近更新 更多