繁体   English   中英

Angular:输入无线电不维护使用 ControlValueAccessor 实现的自定义组件中的选择

[英]Angular: Input radios not maintaining selection in custom component implemented with ControlValueAccessor

我一直在尝试使用 Angular 中的ControlValueAccessor创建一个自定义无线电组件。

在主组件中,我有一个表单,它有两个表单控件,它们与我创建的自定义无线电组件相关联。 在初始加载中,两个自定义无线电组件都被选中,但是一旦我更改值,只有一个自定义无线电保持选定状态。

初始负荷

更改值后

Stackblitz 最小复制

似乎有一个问题导致了这种异常行为,我无法弄清楚。 我在实现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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM