開発覚書はてな版

個人的な開発関連の備忘録

【Angular】双方向バインディングの実装(ControlValueAccessor使用版)

概要

今回はControlValueAccessorを使用した双方向バインディングの実装の仕方を記載します。

通常の双方向バインディングの実装については以下の記事を参照して下さい。 kakkoyakakko2.hatenablog.com

動作環境

  • TypeScript 2.9.x
  • Angular 6.1.x

ControlValueAccessorの利点

  • input系のFormControlのように使用できる
  • [(ngModel)]バインディング
  • Validatorに対応

実装方法

下記のような設定をします。

  • 自作コンポーネント
    • ControlValueAccessorインターフェースを実装する。
    • NG_VALUE_ACCESSORをprovideする。
  • 呼び出し側コンポーネント
    • [(ngModel)]="呼び出し側プロパティ"のように呼び出す。
  • NgModule
    • FormsModuleをimportする。

サンプルソース

custom-list.component.ts
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'custom-list',
  template: `
    <ul>
      <li *ngFor="let num of list"
        (click)="changeValue(num)" [style.background-color]="num === value ? 'yellow' : 'white'">
        {{ num }}
      </li>
    </ul>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomListComponent),
      multi: true
    }
  ]
})
export class CustomListComponent implements ControlValueAccessor {

  /** 選択値 */
  value: number = 0;

  /** 表示リスト */
  list: number[] = [1, 2, 3, 4, 5];

  private fnChange = (_: any) => {};

  private fnTouched = () => {};

  writeValue(value: any): void {
    this.value = value ? value : 0;
  }

  registerOnChange(fn: any): void {
    this.fnChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.fnTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {

  }


  /**
   * 選択値変更処理
   * @param num
   */
  changeValue(num: number): void {
    this.value = num;
    this.fnChange(num);
  }
}
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h2>TwoWay Binding Sample(ControlValueAccessor)</h2>
    <custom-list [(ngModel)]="num"></custom-list>
    <div>num: {{ num }}</div>
  `
})
export class AppComponent {
  num: number = 2;
}
実行結果

f:id:kakkoya:20180901132822g:plain

サンプルソース一式

github.com