データバインディングとは
JavaScript のデータを書き換えると、そのデータに紐づく DOM にその変更が反映され、描画内容が書き換わる仕組みのことをデータバインディングと呼ぶ。つまりデータの更新を DOM の更新に反映させる仕組みのことをいう。
Vue.js には、データバインディングの機能を利用するための構文がいくつか用意されている。例えば、以下のようなものがある。
マスタッシュ構文 {{ }}
テキストコンテンツ(textContent)へのデータバインディングを行うためにはマスタッシュ構文と呼ばれる2重の波括弧 {{ }} を使用する。
先述のサンプルプログラムのように、テンプレート(HTML)のテキストコンテンツの中に2重の波括弧 {{ }} を記述することで、データを描画に反映させることができる。
// HTML
<p>{{ message }}</p>
// オプションオブジェクト
data() {
return {
message: 'Hello world!'
}
}
v-bind ディレクティブ
属性へのデータバインディングを行うためには、v-bind ディレクティブを使用する。
// HTML
<input type="text" v-bind:value="email"><br>
// データオブジェクト
data() {
return {
email: 'foo@example.com'
}
}
単方向データバインディング
正確には、データバインディングは2つの方向で考えることができる。
- JavaScript のデータが変更されたら、それに紐づく DOM を変更する。
- DOM が変更されたら、それに紐づく JavaScript のデータを変更する。
上記のマスタッシュ構文や v-bind ディレクティブは、
- JavaScript のデータが変更されたら、それに紐づく DOM を変更する。
という方向性を持ったデータバインディングを実現するための Vue.js が提供する機能だ。では、その逆方向の
- DOM が変更されたら、それに紐づく JavaScript のデータを変更する。
というデータバインディングを実現するためにはどうすればよいのか。
これには DOM の変更をイベントとして待ち受けて、イベントが発生したら呼び出されるコールバック関数の中で JavaScript のデータを変更することで実現できる。
これを実現する Vue.js が提供する構文として、v-on ディレクティブがある。
v-on ディレクティブ
v-on ディレクティブを使用すると、コールバック関数によるイベントの待ち受け、つまりイベントハンドラーの設定を行なうことができる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>v-on</title>
</head>
<body>
<div id="app">
<p>{{ count }}</p>
<button v-on:click="count++">カウントアップ</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.global.js"></script>
<script>
Vue.createApp({
data() {
return {
count: 0
}
}
}).mount('#app');
</script>
</body>
</html>
このサンプルでは、
// HTML
<button v-on:click="count++">カウントアップ</button>
により <button> 要素でクリックイベントを待ち受け、イベントが発生したら
count++
という処理を実行するハンドラー(コールバック関数)を登録している。これにより、DOM の変化(Click イベント)により JavaScript のデータ(count)を変更することができる。
双方向データバインディング
上記で説明した2方向のデータバインディング
- JavaScript のデータが変更されたら、それに紐づく DOM を変更する。
- DOM が変更されたら、それに紐づく JavaScript のデータを変更する。
を組み合わせることで、双方向のデータバインディングも実現できる。例えば、
- JavaScript のデータが変更されたら、テキストボックスの表示を変更する。
- テキストボックスにテキストが入力されたら、JavaScript のデータに格納する。
という双方向のデータバインディングを考えてみる。
// HTML
<input type="text">
// オプションオブジェクト
data() {
return {
message: ''
}
}
まずは、
- JavaScript のデータが変更されたら、テキストボックスの表示を変更する。
こちらの実装を考えよう。これは v-bind ディレクティブを使用して、<input>要素の value 属性にデータ message をバインディングしてやれば実現できる。
// HTML
<input type="text" v-bind:value="message">
このバインディングが上手くいっていることを確認するために、ボタンを追加して、Click イベントで message の文字列に ‘clicked’ を連結することで message が変化するようにしてみる。また、message が格納している文字列を見えるようにもしておく。
// HTML
<input type="text" v-bind:value="message"><br>
<button v-on:click="message += 'clicked'">クリック</button><br>
{{message}}
次に逆方向のデータバインディング
- テキストボックスにテキストが入力されたら、JavaScript のデータに格納する。
の実装を考えよう。これには先ほど説明した v-on ディレクティブを使用する。
またテキストボックスの入力値を取得する必要があるが、通常の JavaScript であれば、イベントハンドラーとして設定したコールバック関数の中で、その引数で受け取るイベントオブジェクトから入力値を取得するだろう。Vue.js の v-on:イベント名=”JavaScript の式” という記述では、JavaScript の式の中でイベントオブジェクトを $event で参照することができる。(参考ページ)
このことから次のように書くことができる。
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
<input>要素の入力値の変更を input イベントで待ち受けるように設定した。
プログラム全体は次のようになった。双方向のデータバインディングが実現できていることを確認することができる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>data binding</title>
</head>
<body>
<div id="app">
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value"><br>
<button v-on:click="message += 'clicked'">クリック</button><br>
{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.global.js"></script>
<script>
Vue.createApp({
data() {
return {
message: ''
}
}
}).mount('#app');
</script>
</body>
</html>
v-model ディレクティブ
上記の双方向データバインディンを実現した <input>要素の記述
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
は、<input>要素の value 属性の値と Vue インスタンスのデータ message を双方向にバインディングしている。Vue.js には
v-bind:value="message" v-on:input="message = $event.target.value"
という双方向データバインディンの記述と同等な機能を与える、よりシンプルな構文が存在する。それが v-model ディレクティブであり、v-model ディレクティブを使用するとこの記述は
v-model="message"
と書き換えることができる。したがって <input>要素は次のように書き換えることができる。
<input type="text" v-model="message">