データバインディングとはView(HTMLテンプレート)とコンポーネント(Typescriptファイル)間でデータをやりとりする仕組みす。
データバインディングを使うことで最新のデータを自動でViewに反映させることが出来ます。
データバインディングは一方向にデータを送信する片方向データバインディングと、双方向にデータを送信しあう双方向データバインディングの2つに分けられます。
片方向データバインディングにはコンポーネントからViewにデータを送るバイディングと反対にViewからコンポーネントにデータを送るバイディングがあります。
更にコンポーネントからViewにデータを送るバイディングには、interpolation/ Property/ Attribute/ Class/ Styleに種類が分かれます。
今回それぞれのバイディングの使い方について、例を挙げながらを解説して行きます。
この記事を読み終わるとデータバインディングとは何か、どのように使うのかの基礎を押さえることが出来ます。
片方向バインディング
まずはコンポーネントからViewへのデータバインディングを見ていきましょう。
コンポーネントからViewへのデータバインディング
上で紹介した通り、コンポーネントからViewへのデータバインディングには5つの種類があります。
どれも大切なのでしっかりと理解しましょう。
Interpolation
interpolationはコンポーネントで宣言したstring型の変数をHTMLテンプレートに自動でバインディングしてくれます。
HTMLテンプレート内では、宣言した変数を二重中括弧の中に入れます。
interpolationはタグ要素のテキストの代わりに入れることも出来ますし、要素のプロパティの値として入れることもできます。
コンポーネントファイル
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//string型の変数を宣言する
name= "Ichiro TANAKA";
}
HTMLファイル
<!-- 要素内テキストの代わりに入れる -->
<div>{{ name }}</div>
<!-- 要素のプロパティの値として入れる -->
<input type="text" value="{{ name }}">
ちゃんとViewに変数の値Ichiro TANAKAがバインディングされました。
Propertyバインディング
AngularではDOM要素のプロパティの値をバインディングすることができます。
プロパティの値がstring型の場合、もちろん上の例ように、interpolationを使えばいいのですが、interpolationはstring型の変数のみに対応出来るので、string型以外の場合は、プロパティバインディングを使います。
プロパティバインディングはHTMLテンプレート内で下のように表現します。
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//boolean型の変数を宣言する
btnAbled= false;
btnDisabled= true;
}
HTMLファイル
<!-- ボタンのdisabledというプロパティを角括弧で囲み
コンポーネントで宣言したboolean変数btnDisabledを代入する -->
<button [disabled]="btnAbled">Disabled button</button>
<hr>
<!-- ボタンのdisabledというプロパティを角括弧で囲み
コンポーネントで宣言したboolean変数btnDisabledを代入する -->
<button [disabled]="btnDisabled">Disabled button</button>
一番目のボタンは変数btnAbledの値をfalseにしているのでボタンが有効に、二番目のボタンは変数btnDisabledの値をtrueにしているので、ボタンが無効になっています。
Attributeバインディング
上の例で見たとおり、PropertyバインディングはDOM要素のプロパティに対してバインディングされるのに対し、AttributeバインディングはHTML属性に対するバインディングです。
プロパティを持たない属性に対して使います。
例としてcolspanがあります。
colspanをプロパティバインディングのように[colspan]=”変数”とするとエラーになります。
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//colspanVal変数を宣言する
colspanVal=2;
}
HTMLファイル
<table>
<th>1行目</th>
<th>2行目</th>
<th>3行目</th>
<tr>
<td>データ1</td>
<td>データ2</td>
<td>データ3</td>
</tr>
<tr>
<!-- ここをプロパティバインディング[colspan]="colspanVal"にすると
エラーが出る -->
<td [attr.colspan]="colspanVal">データ4</td>
<td>データ5</td>
</tr>
</table>
データ4のcolspanの変数に2を代入したので、データ4が2つ分の枠を締めました。
Classバインディング
Classバインディングはクラスの有無をBooleanによって可能にします。
SASS(CSS)
@charset "utf-8"
.myClass
background-color: yellowgreen
div
margin: 5px
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//SASSで作ったクラス(myClass)を変数に代入する
myClassVal= 'myClass'
//classActiveというboolean変数を宣言する
classActive=true;
//classNotActiveというboolean変数を宣言する
classNotActive=false;
}
HTMLファイル
<div class="myClass">普通のSASSの文です</div>
<div [class]="myClassVal">クラスをプロパティバインディングした文です</div>
<div [class.myClass]="classActive">クラスバインディングActiveの文です</div>
<div [class.myClass]="classNotActive">クラスバインディングNotActiveの文です</div>
SASS(CSS)でmyClassというクラスを作ります。
一番目はmyClassを通常通り、クラスプロパティの値として設定しました。
二番目はクラスプロパティをバインディングし、コンポーネントで宣言した変数myClassValを値として設定しました。
myClassValにはSASSクラスのmyClassが代入されています。
三番目はmyClassをクラスバインディングし、コンポーネントで宣言した変数classActiveを値として設定しています。
classActiveの値がtrueなので、myClassが反映されています。
三番目はmyClassをクラスバインディングし、コンポーネントで宣言した変数classNotActiveを値として設定しています。
classNotActiveの値がfalseなので、myClassが反映されていません。
Styleバインディング
Styleバインディングはその名の通り、HTMLテンプレートでスタイルデータを
受け取ることが出来ます。
[style.スタイルプロパティ]=”スタイルプロパティの値もしくは変数”
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//fontColorという変数を宣言し、値redを代入します。
fontColor="red"
}
HTMLファイル
<div style="color: green">通常のStyleの設定です</div>
<div [style.color]="'blue'">スタイルバインディンの値に直接blueを入れた文です</div>
<div [style.color]="fontColor">スタイルバインディンの値に変数fontColorを入れた文です</div>
一番目の要素には通常通りのStyleを設定しました。 二番目の要素には、フォントカラーをスタイルバインディングし、直接値blueを入れました。
二番目のように直接値を入れる場合、通常通りのスタイル設定と同じ機能をもつので、バインディングの意味があまりないようです。
三番目の要素はフォントカラーをスタイルバインディングし、コンポーネントで宣言した変数fontColorを入れました。
変数の値をコンポーネントで変更することでViewのコントロールが可能になります。
Viewからコンポーネントへのデータバインディング
ViewからコンポーネントへのデータバインディングにはEventバインディングあります。
画面上でユーザーが操作を行うことにより、データがコンポーネントに送られます。
Viewからコンポーネントへのデータバインディングには丸括弧の中にイベントを記載し、値としてイベントがもたらすファンクション名を記載します。
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
//fontColorという変数を宣言し、値redを代入します。
fontColor="red";
backgroundColor="lightgreen";
//フォントカラーを変更するファンクション
changeFontColor(){
this.fontColor="green";
}
//バックグラウンドカラーを黄色にするファンクション
backgroundColorToYellow(){
this.backgroundColor="yellow"
}
//バックグラウンドカラーを緑にするファンクション
backgroundColorToGreen(){
this.backgroundColor="lightgreen"
}
}
コンポーネントファイル内で変数fontColorにredを代入、backgroundColorにlightgreenを代入します。
また、changeFontColor/ backgroundColorToYellow/ backgroundColorToGreenという3つのファンクションを作り、上で作った変数の値を他の色に変更するよう設定します。
HTMLファイル
<div
<!-- フォントカラーを設定するスタイルバインディング -->
[style.color]="fontColor"
<!-- バックグラウンドカラーを設定するスタイルバインディング -->
[style.backgroundColor]="backgroundColor"
<!-- マウスを要素の上に置いた時、発生するファンクション -->
(mouseover)="backgroundColorToYellow()"
<!-- マウスを要素の上から離した時、発生するファンクション -->
(mouseout)="backgroundColorToGreen()">
フォントカラーとバックグラウンドカラーを変更できます。
</div>
<!-- ボタンクリックで発生するファンクション -->
<button (click)="changeFontColor()">フォントカラー変更</button>
div要素にコンポーネントで作った変数数fontColorと、backgroundColorをスタイルバインディングします。
また、カーソルが要素の上に来た時にバックグラウンドカラーを黄色にするイベント(mouseover event)とカーソルが要素の上から外れたときに緑にするイベント(mouseout event)をバインディングします。
それぞれの値として、コンポーネントで作ったファンクションbackgroundColorToYellow/ backgroundColorToGreenを代入します。
button要素にはフォントカラーを変更するイベント(click event)をバインディングします。
最初は上のような状態です。
ボタンをクリックするとフォントカラーが緑に変更されました。
また、カーソルをdiv要素の上に持っていくとバックグラウンドカラーが黄色になりました。
ユーザーがViewからアクションすることにより、コンポーネントのデータ(変数)を書き換えました。
双方向バインディング
双方向バインディングではViewから送信したデータがコンポーネントで受け取られ、コンポーネント内で処理をした後に再度、最新のデータをViewに返します。
つまり、上で解説したPropertyバインディングとEventバインディングをかけ合わせたバインディングです。
AngularではPropertyバインディングとEventバインディングの両方を記載しなくていいようにngModelという双方向データバインディングのシンタックスが用意されています。
Errorメッセージ
Error: src/app/app.component.html:11:20 - error NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'.
11 <input type="text" [(ngModel)]="userName">
src/app/app.component.ts:5:16
5 templateUrl: './app.component.html',
Error occurs in the template of component AppComponent.
ngModelを使っているコンポーネントが宣言してあるモジュールファイル内にFormsModuleをインポートすることでエラーを解決できます。
App moduleファイル
import { NgModule } from '@angular/core';
//ここにFormsModuleを追加
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; @NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
//ここにFormsModuleを追加
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
では早速Inputを使って、例を見ていきましょう!
コンポーネントファイル
import { Component } from '@angular/core'; @Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
})
export class AppComponent {
userName1: string="Taro"
userName2: string="Hanako" updateValue(event: any){
this.userName1= event.target.value;
}
}
コンポーネント内でuserName1、userName2というstring変数を宣言し、TaroとHanakoという名前を代入します。
また、Input内に入力された文字を入れるupdateValueというファンクションも作成しました。
HTMLファイル
<p>プロパティバインディングとイベントバインディングの両方を使った場合 </p>
<input type="text" [value]="userName1" (input)="updateValue($event)">
<div> {{userName1}} </div>
<hr>
<p>ngModelを使った場合 </p>
<input type="text" [(ngModel)]="userName2">
<div> {{userName2}} </div>
一番目のinput要素では、valueプロパティをバインディングすることで([value]=”userName1”)、コンポーネントで宣言した変数userName1をinputの値としてViewに反映させています。
また、inputイベントをバインディングすることで((input)=”updateValue($event)”)、inputに入力された文字の情報をパラメータで受け取るよう設定します。
div要素にinterpolationとして変数userName1を入れることで、変更後の値(データ)を確認することが出来ます。
二番目のinput要素では、双方向バインディングのngModelをバインディングし、値としてコンポーネントで宣言したstring変数userName2を代入しています。
サイト立ち上げ時は、コンポーネントで宣言した値、TaroとHanakoがinputとinterpolationの中に入力されています。
一番目、二番目の両方のinput要素において、コンポーネントで宣言した変数のデータ→Viewへ送られたということが分かります。
input内にそれぞれIchiro、Aikoと入力するとinterpolationにも反映されました。
inputでユーザーが入力したデータがView→コンポーネントへ送られ、コンポーネントで変数userName1、userName2が書き換えられ、更に変数のデータ→Viewへ送られたということが分かります。
一番目も二番目も同じ機能を果たしています。
このことから双方向バインディングが必要な場合、プロパティとイベントバインディングを使うのではなく、ngModelを使う方がコードが短く便利なことが分かりますね。
まとめ
データバインディングの使い方、いかがでしたか。
プロパティバインディングやイベントバインディング、双方向バインディングは特に良く使われるので、何度も読んで理解を深めてもらえると嬉しいです。