ControlValueAccessorでRadioボタンを含むコンポーネントを実装するメモ

  • (change)を利用
  • ViewChildrenを利用
  • ref.nativeElement.checked = true;でViewを更新

RadioComponent

import { Component, ElementRef, QueryList, ViewChildren } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-radio',
  template: `
  <label *ngFor="let value of values">
    <input type="radio"
      (change)="onChange($event.target.value)"
      name="radio" [value]="value" #radio>{{value}}
  </label>
  <pre></pre>
 `,
  styles: [`:host {
    display: block;
    background-color: #fcc;
  }`],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: RadioComponent,
      multi: true
    }
  ]
})
export class RadioComponent implements ControlValueAccessor {
  @ViewChildren('radio') private radios: QueryList<ElementRef>;
  values = ['foo', 'bar', 'baz'];

  onChange: (value: string) => void;
  onTouched: () => void;

  // ControlValueAccessor
  writeValue(value: string) {
    if (value) {
      const target = this.radios.find(radio =>
        radio.nativeElement.value
      );
      if (target) {
        toChecked(target);
      }
    }
  }
  registerOnChange(fn: any) { 
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }
}

function toChecked(ref: ElementRef) {
  ref.nativeElement.checked = true;
}

利用例

<app-radio name="radio" [ngModel]="(form$ | async).radio"></app-radio>