# CH2 データの登録と更新
# S08 テキストと属性のデータバインディング
54~66ページ
# オブジェクトや配列要素の表示
55ページ
<!-- 1 オブジェクトのプロパティを表示 -->
<p>{{ message.value }}</p>
<!-- 2 文字列の長さを表示 -->
<p>{{ message.value.length }}</p>
<!-- 3 リストのインデックス2を表示 -->
<p>{{ list[2] }}</p>
<!-- 4 プロパティを組み合わせて使用 -->
<p>{{ list[num] }}</p>
new Vue({
el: '#app',
data: {
// オブジェクトデータ
message: {
value: 'Hello Vue.js!'
},
// 配列データ 3 と 4 で使用
list: ['りんご', 'ばなな', 'いちご'],
// 別のデータを使用してlistから取り出す要素を動的に 4 で使用
num: 1
}
})
# クリックでカウンターを増やそう
59ページ
<div id="app">
<!-- countプロパティを表示する -->
<p>{{ count }}回クリックしたよ! </p>
<!-- このボタンをクリックするとincrementメソッドが呼び出される -->
<button v-on:click="increment">カウントを増やす</button>
</div>
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
// ボタンをクリックしたときのハンドラ
increment: function () {
this.count += 1 // 処理は再代入するだけでOK!
}
}
})
0回クリックしたよ!
# クラスとスタイルのデータバインディング
62ページ
<button v-on:click="isActive=!isActive">isActiveを切り替える</button>
<p v-bind:class="{ child: isChild, 'is-active': isActive }" class="item">
動的なクラス
</p>
<p v-bind:style="{ color: textColor, backgroundColor: bgColor }" class="item">
動的なスタイル
</p>
new Vue({
el: '#app',
data: {
isChild: true,
isActive: true,
textColor: 'red',
bgColor: 'lightgray'
}
})
.item {
padding: 4px 8px;
transition: background-color 0.4s;
}
.is-active {
background: #ffeaea;
}
動的なクラス
動的なスタイル
※ 分かりやすいように .is-active
は背景色を変えています
# SVGのデータバインディング
65ページ
<div id="app">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="75" v-bind:r="radius" fill="lightpink" />
</svg>
<input type="range" min="0" max="100" v-model="radius">
</div>
new Vue({
el: '#app',
data: {
radius: 50
}
})
TIP
もしも、受け取った radius
の値を数値データ型として数式に使用する場合は .number
修飾子を使用しましょう。
<input type="range" min="0" max="100" v-model.number="radius">
# S10 リストデータの表示と更新
70~84ページ
# 要素を繰り返し描画する
「繰り返し描画しながら、さまざまな条件を適用する」の部分も一緒にまとめています。
70~74ページ
<div id="app">
<ul>
<li v-for="item in list"
v-bind:key="item.id"
v-bind:class="{ tuyoi: item.hp > 300 }">
ID.{{ item.id }} {{ item.name }} HP.{{ item.hp }}
<span v-if="item.hp > 300">つよい!</span>
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
}
})
- ID.1 スライム HP.100
- ID.2 ゴブリン HP.200
- ID.3 ドラゴン HP.500 つよい!
TIP
テンプレート中の item.hp > 300
というような条件に一致するかを調べる式は、メソッドや算出プロパティにすると分かりやすくなり保守もしやすくなります。
また、ディレクティブの式の部分は、その要素を包含しているコンポーネントの仮想 DOM に変化があったとき毎回呼び出されます。(v-on
はコールバック関数なので除く)
書籍で説明しているカスタムディレクティブの動作と同じで、直接関係のないデータに変化があったときにも呼び出されます。
<li v-for="item in list" v-if="isTuyoi(item.hp)" ...>
{{ 1+1 }}
多くの状態に依存しているコンポーネントでは、無駄な処理が発生しやすくなるため注意しましょう!
なるべく算出プロパティや v-once
を活用したり、一定の単位でコンポーネントに分割するのがオススメです。
# リストに要素を追加しよう
75ページ
<div id="app">
<!-- このフォームの入力値を新しいモンスターの名前に使う -->
名前
<input v-model="name">
<button v-on:click="doAdd">モンスターを追加</button>
<ul>
<li v-for="item in list" v-bind:key="item.id">
ID.{{ item.id }} {{ item.name }} HP.{{ item.hp }}
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
name: 'キマイラ',
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
},
methods: {
// 追加ボタンをクリックしたときのハンドラ
doAdd: function () {
// リスト内で1番大きいIDを取得
var max = this.list.reduce(function (a, b) {
return a > b.id ? a : b.id
}, 0)
// 新しいモンスターをリストに追加
this.list.push({
id: max + 1, // 現在の最大のIDに+1してユニークなIDを作成
name: this.name, // 現在のフォームの入力値
hp: 500
})
}
}
})
- ID.1 スライム HP.100
- ID.2 ゴブリン HP.200
- ID.3 ドラゴン HP.500
# リスト要素を削除しよう
77ページ
<div id="app">
<ul>
<li v-for="(item, index) in list" v-bind:key="item.id">
ID.{{ item.id }} {{ item.name }} HP.{{ item.hp }}
<!-- 削除ボタンをv-for内に作成 -->
<button v-on:click="doRemove(index)">モンスターを削除</button>
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
},
methods: {
// 要素を削除ボタンをクリックしたときのハンドラ
doRemove: function (index) {
// 受け取ったインデックスの位置から1個要素を削除
this.list.splice(index, 1)
}
}
})
- ID.1 スライム HP.100
- ID.2 ゴブリン HP.200
- ID.3 ドラゴン HP.500
# リスト要素プロパティを更新しよう
81ページ
<div id="app">
<ul>
<li v-for="(item, index) in list" v-bind:key="item.id" v-if="item.hp">
ID.{{ item.id }} {{ item.name }} HP.{{ item.hp }}
<span v-if="item.hp < 50">瀕死!</span>
<!-- ボタンはv-for内に作成 -->
<button v-on:click="doAttack(index)">攻撃する</button>
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: 'スライム', hp: 100 },
{ id: 2, name: 'ゴブリン', hp: 200 },
{ id: 3, name: 'ドラゴン', hp: 500 }
]
},
methods: {
// 攻撃ボタンをクリックしたときのハンドラ
doAttack: function (index) {
this.list[index].hp -= 10 // HPを減らす
}
}
})
- ID.1 スライム HP.100
- ID.2 ゴブリン HP.200
- ID.3 ドラゴン HP.500
# 外部からデータを取得する
83ページ
このサンプルコードでは axios を使用しています。「axios.min.js」を読み込んでください。
[
{ "id": 1, "name": "スライム", "hp": 100 },
{ "id": 2, "name": "ゴブリン", "hp": 200 },
{ "id": 3, "name": "ドラゴン", "hp": 500 }
]
<div id="app">
<ul>
<li v-for="(item, index) in list" v-bind:key="item.id">
ID.{{ item.id }} {{ item.name }} HP.{{ item.hp }}
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
// あらかじめ空リストを用意しておく
list: []
},
created: function () {
axios.get('list.json').then(function (response) {
// 取得完了したらlistリストに代入
this.list = response.data
}.bind(this)).catch(function (e) {
console.error(e)
})
}
})
TIP
HTML ファイルを 「file://」 というスキームを使ってブラウザで開いている場合、セキュリティの問題でエラーになります。 次のような方法をお試しください。
ファイルを適当なホスティングサーバーにアップロードして呼び出す。
XAMPP や、Docker の「hello-world-nginx」などを利用して
localhost
などで呼び出す。myjson.com のようなサービスを利用するのもお手軽です。
Chrome なら
--allow-file-access-from-files
オプションを付けてブラウザを起動してください。 ※ セキュリティ上、このオプションを付けたまま通常のブラウジングはしないでください
# S11 DOMを直接参照する$elと$refs
85ページ
# $elや$refsは一時的な変更!
86ページ
<div id="app">
<button v-on:click="handleClick">カウントアップ</button>
<button v-on:click="show=!show">表示/非表示</button>
<span ref="count" v-if="show">0</span>
</div>
new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
var count = this.$refs.count
if (count) {
count.innerText = parseInt(count.innerText, 10) + 1
}
}
}
})
# S12 テンプレート制御ディレクティブ
88ページ
# v-cloak
91ページ
<div id="app" v-cloak>
{{ message }}
</div>
@keyframes cloak-in {
0% {
opacity: 0;
}
}
#app {
animation: cloak-in 1s;
}
#app[v-cloak] {
opacity: 0;
}