はじめに
自作コンポーネントで双方向バインディング機能(ngModel
)を使えるようにしたい。
環境
CSSフレームワークとしてAngular Materialを使用しています
- Angular: 8.2.14
- Node: 12.13.1
- Angular Material: 8.2.3
今回作るもの
双方向バインディング機能([(ngModel)]
)を持った分数入力用のコンポーネントを作成します。
分数入力コンポーネントの実装
カスタムコンポーネントに[()]
構文を使用するには、@Input
プロパティx
と@Output
プロパティxChange
を実装する必要があります。
fraction-input.component.ts
import { Component, EventEmitter, OnInit, Input, Output } from '@angular/core'; import { FormGroup, FormControl, FormBuilder } from '@angular/forms'; import { Fraction } from 'src/app/entity/fraction'; /** * 分数入力用コンポーネント * * @export * @class FractionInputComponent * @implements {OnInit} */ @Component({ selector: 'app-fraction-input', templateUrl: './fraction-input.component.html', styleUrls: ['./fraction-input.component.scss'], }) export class FractionInputComponent implements OnInit { @Input() fraction: Fraction = new Fraction(); @Output() fractionChange = new EventEmitter<Fraction>(); public fractionFormGroup: FormGroup; // 分子 public numeratorControl: FormControl; // 分母 public denominatorControl: FormControl; constructor(private fb: FormBuilder) {} public ngOnInit() { this.createForm(); this.numeratorControl = this.fractionFormGroup.get( 'numerator' ) as FormControl; this.denominatorControl = this.fractionFormGroup.get( 'denominator' ) as FormControl; } /** * reactive formの設定 * * @private * @memberof FractionInputComponent */ private createForm() { this.fractionFormGroup = this.fb.group({ numerator: ['', []], denominator: ['', []], }); } }
fraction-input.component.html
<form [formGroup]="fractionFormGroup"> <div class="fraction-group"> <!-- 分子 --> <mat-form-field> <input matInput class="input" placeholder="分子" formControlName="numerator" [(ngModel)]="fraction.numerator"> </mat-form-field> <div class="split">/</div> <!-- 分母 --> <mat-form-field> <input matInput class="input" placeholder="分母" formControlName="denominator" [(ngModel)]="fraction.denominator"> </mat-form-field> </div> </form>
app.component.html
<div class="wrapper"> <mat-card class="form-card"> <div>分数</div> <!-- 分数コンポーネント --> <app-fraction-input [(fraction)]="fraction"></app-fraction-input> {{fraction | json}} </mat-card> </div>
FractionInputComponent
に@Input() fraction
と@Output() fractionChange
を実装することでAppComponent
との間に双方向バインディングを実現しています([(fraction)]
)。
なお、AppComponent
とFractionInputComponent
間ではFraction
オブジェクトを双方向にバインドしています。
fraction.ts
/** * 分数オブジェクト * * @export * @class Fraction */ export class Fraction { // 分子 public numerator: number; // 分母 public denominator: number; constructor() { this.numerator = undefined; this.denominator = undefined; } }
作成したコンポーネントを動作させると次のキャプチャーのようになり、子コンポーネントに入力した値が双方向バインディングによって親コンポーネントに反映されていることがわかります。
参考サイト
双方向バインディング(ngModel)対応のComponentを自作する - Qiita
[Angular] カスタムコンポーネント(Custom Component)で ngModel を使う - Qiita
How to create custom input component with ngModel working in angular 6? - Stack Overflow