きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × SIer

Angular × データバインド × いろいろなバインド方法を整理してみた

この記事の目的

この記事は、
Angular のいろいろなバインド方法と種類を自分なりにまとめること
を目的としています。

本題

データバインディングの種類がいろいろあってわからない。

コンポーネント→Viewへのバインディング {{ }} 型
//template
{{interpolationValue}}

//Component
public interpolationValue: string = "Public";
private interpolationValuePrivate: string = "Private";
  • Componentのクラス変数として定義したものをバインドできる。
  • アクセス修飾子は、PrivateでもPublicでもバインドできた。
  • 注意事項↓

Angular では、 目 に 見える 以上 に{{...}} 式 を 何度 も 評価 し ます。 時間 の かかる 式 は、 そのまま アプリ の 速度 低下 に つながる 可能性 が ある ので、 注意 し て ください。
山田 祥寛. Angularアプリケーションプログラミング (Kindle の位置No.1191-1192).

コンポーネント→Viewへのバインディング プロパティバインド型
< img [src]=" parameter" />
< img bind-src="parameter" />
< img [src]=" {{parameter}}" />
  • コンポーネントのプロパティに値をバインドする際は、プロパティバインド型のバインドを行う。
  • 構文は3種類あるが、どれを使っても同じ。
View→コンポーネントへのバインディング
  • イベントを利用して値を取得する。
Viewと、コンポーネントの間での双方向バインディング
//HTML
<mat-form-field class="example-form-field">
  <mat-label>Clearable input</mat-label>
  <input matInput type="text" [(ngModel)]="Paramter">
</mat-form-field>

//コンポーネント
public Paramter = "Test";
  • Viewの値と、コンポーネントのプロパティをバインドができる。
  • [(ngModel)]にコンポーネント側のプロパティを設定することで、バインドが可能。
  • 入力チェックや、入力チェック時のエラー内容を表示したりする場合、Form Controlを利用すると、コードがすっきりする。※イベントバインディングでも同じことができる。

その他のバインディング方法は?

属性バインディング
  • Angularでサポートされていない、標準のHTML要素の属性に値を設定する際に利用する。ex.

angular.jp

クラスバインディング
  • 動的にクラスを変更する際に利用する。
  • Styleをクラス名で引っ掛けている場合に、動的に見た目を変更したりできる。あれ、スタイルバインドとの違いは・・・?

angular.jp

スタイルバインディング
  • 動的にStyleを変更する際に利用する。

angular.jp

イベントバインディング
  • on click などのイベントをバインドするときに利用する。

angular.jp

コンポーネントへのデータバインディングってどうやるの?

コンポーネントとの片方向バインディング
  • @Inputを定義して、親から子を呼び出すときに渡す。
  • プロパティバインド型のバインドを利用する。
コンポーネントとの双方向バインディング
  • @Input/outputを駆使して、同期処理を実装する。
  • [(ngModel)]みたいな動きになる実装もできる

qiita.com

参照元記事でもやられていますが、実際にやってみました。
やってみた結果、以下のコードで、親子間でのデータバインドを双方で実現することができました。これは便利。ただ、この実装では、子のInputに対しては、値の入力が確定して、かつ、値が変更されたタイミングでのみ親に変更が伝わりますが、親から子へは、値の変更がリアルタイムで伝わるので、値の伝わる向きによってタイムラグが出てしまいます。これは、子コンポーネントのEmitタイミングの調整が必要です。

//親コンポーネント用html
<sample-component [(item)]="Paramter"></sample-component>
<mat-form-field> <- 以下は確認用のInput要素
	<mat-label>Parent Input</mat-label>
	<input matInput
    [(ngModel)]="Paramter"
    (change)="change($event.target.value)">
</mat-form-field>
//親コンポーネントの実装は特になし
//子コンポーネント用html
<mat-form-field>
	<mat-label>Child Input</mat-label>
	<input matInput
    [(ngModel)]="item" ←@Inputを双方向バインドする。
    (change)="change($event.target.value)">
</mat-form-field>
//子コンポーネント
  @Input() item: string;
  @Output() itemChange = new EventEmitter<string>();
  
  change(value: any) {
    this.item = value;
    this.itemChange.emit(this.item);
  }

番外編)バインディングするときに詰まったこと

以下のような実装をした時、バインドが実行されず、詰まりました。

<form class="example-form">
  <mat-form-field class="example-full-width">
    <mat-label>Sample Input</mat-label>
    <input matInput 
    placeholder="Ex. Pizza" 
    [(ngModel)]="Paramter">
  </mat-form-field>
</form>

エラー内容は以下の通りです。

ERROR
Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.

Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">

Angularのエラーメッセージは、どう直せばいいか丁寧に教えてくれるので、助かります。理由をちゃんと理解していませんが、このような場合(Formタグ内でngModelのバインディングを利用する場合)、name属性の指定が必須となるようです。このことから、Formタグを外して利用するか、上記2例を参考に実装することで、ngModelを利用した双方向バインディングが利用できるようになることがわかります。FormGroupを利用しないのであれば、Formタグでくくる必要もないはずなので、Formタグを外しても害はない。はず。修正後の実装は以下の通りです。

//FORMタグを外す
<mat-form-field class="example-full-width">
  <mat-label>Sample Input</mat-label>
  <input matInput 
  name="SampleInput"
  placeholder="Ex. Pizza" 
  [(ngModel)]="Paramter">
</mat-form-field>


//Name属性を付ける。
<form class="example-form">
  <mat-form-field class="example-full-width">
    <mat-label>Sample Input</mat-label>
    <input matInput 
    name="SampleInput"
    placeholder="Ex. Pizza" 
    [(ngModel)]="Paramter">
  </mat-form-field>
</form>


以上です。