Help us understand the problem. What is going on with this article?

Angularでカスタムフォーム(CustomFormControls)を作る(1/2)

More than 1 year has passed since last update.

Angulerで標準のコンポーネントをカスタマイズして自前のフォームを作るやり方です。

AngularConnect2017の動画を公式のドキュメントとして参考にしています。
https://youtu.be/ZNTsdaZiqP8?t=9896

やりたいこと

上記の動画にあわせて、入力必須のテキストフィールドコンポーネントを作って行きます。

  • 標準のinputなどのフォームと同様の使い方ができること
  • 入力必須のバリデーションが行われること
  • 未入力の場合にエラーメッセージを表示されること

スクリーンショット 2018-07-11 11.45.41.png

コンポーネントのひな型

まずはこれから実装していくコンポーネントの最初の状態です。

requiredText.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-required-text',
  templateUrl: './requiredText.component.html',
  styleUrls: [ './requiredText.component.css' ],
})
export class RequiredTextComponent implements ControlValueAccessor {  
}
requiredText.component.html
<div>
  <input type="text">
</div>

ControlValueAccessor

カスタムコンポーネントを作るにあたり、下の例のようにngModelやFormControlのプロパティを標準のフォームのように使えるようにします。

<custom-input [(ngModel)]="name"></custom-input>

ngModelやFormControlのプロパティからデータをバインドさせるようにするには、「ControlValueAccessor」をimplmentさせます。これは標準のinputやtextareaなどすべてのフォームがimplementしているもので、新しくフォームを作る際にもimplementを行います。

requiredText.component.ts
export class RequiredTextComponent implements ControlValueAccessor 

Interface Methodの実装

ControlValueAccessorをimplemntを行ったので、インターフェースで定義されている以下のメソッドを実装します。

  • writeValue(obj: any): void {}
  • registerOnChange(fn: any): void {}
  • registerOnTouched(fn: any): void {}
  • (Optional) setDisabledState(isDisabled: boolean){}

writeValue

モデルの値をViewのフォームに設定します。
まず、Viewのフォームを取得するためにDOM側に#でIDを設定します。
モデル側ではViewChildでID参照して要素を取得できる状態にします。

requiredText.component.html
<input type="text" #input>
requiredText.component.ts
@ViewChild('input') private input: ElementRef;

writeValue(obj: any): void {
  this.input.nativeElement.value = obj;
}

registerOnChange

Viewでの変更時のコールバックを登録します。
onChangeのメソッドを定義し、View側で変更が起きたらonChangeを呼ぶようにします。
registerOnChangeでonChangeのからコールバックを呼び出すようにします。

requiredText.component.html
<input type="text" (input)="onChange($event.target.value)" #input>
requiredText.component.ts
onChange: (obj: any) => void;

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

registerOnTouched

ViewでのTouch(blur)イベントのコールバックを登録します。
registerOnChangeと同様に、onTouchedメソッドを定義しregisterOnTouched内でコールバックを呼び出すようにします。

requiredText.component.html
<input type="text" #input 
 (input)="onChange($event.target.value)" 
 (blur)="onTouched()">

requiredText.component.ts
onTouched: (obj: any) => void;

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

setDisabledState

フォームの活性/非活性をViewに設定します。

requiredText.component.html
<input type="text" #input 
 (input)="onChange($event.target.value)" 
 (blur)="onTouched()" 
 [disabled]="disabled">
requiredText.component.ts
disabled: boolean

setDisabledState(isDisabled: boolean){
 this.disabled = isDisabled;
}

Provider

さらにControlValueAccessorをimplementした際には、ValueAccessorをDIする必要があります。

providers: [
   {
      provide: NG_VALUE_ACCESSOR,
      useExisting: CustomRadioFormControlComponent,
      multi: true,
    },
  ]

ここまでのDEMO

https://stackblitz.com/edit/angular-custom-form-control-k

自作したapp-required-textにngModelを設定し、2wayバインドができていることが確認できます。
もちろんReactiveFormにしてFormControlを渡して使用することができます。

app.component.html
<app-required-text [(ngModel)]="name"></app-required-text>
  {{name}}

スクリーンショット 2018-07-10 14.54.34.png

次回に続きます。

参考

kitagawamac
フロントエンドエンジニア speakerdeck https://speakerdeck.com/kitagawa312
toreta
飲食店向け予約/顧客台帳サービス「トレタ」、超直前予約アプリ「トレタnow」を開発・提供するスタートアップ企業です。
https://corp.toreta.in
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした