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!
    }
  }
})
DEMO

0回クリックしたよ!

guide-ch2-demo01

クラスとスタイルのデータバインディング

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;
}
DEMO

動的なクラス

動的なスタイル

※ 分かりやすいように .is-active は背景色を変えています

guide-ch2-demo02

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
  }
})
DEMO
guide-ch2-demo03

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 }
    ]
  }
})
DEMO
  • ID.1 スライム HP.100
  • ID.2 ゴブリン HP.200
  • ID.3 ドラゴン HP.500 つよい!
guide-ch2-demo04

TIP

テンプレート中の item.hp > 300 というような条件に一致するかを調べる式は、メソッドや算出プロパティにすると分かりやすくなり保守もしやすくなります。

また、ディレクティブの式の部分は、その要素を包含しているコンポーネントの仮想 DOM に変化があったとき毎回呼び出されます。(v-on はコールバック関数なので除く) 書籍で説明しているカスタムディレクティブの動作と同じで、直接関係のないデータに変化があったときにも呼び出されます。

毎回 isTuyoi() を呼ぶ
<li v-for="item in list" v-if="isTuyoi(item.hp)" ...>
毎回 1+1 をする
{{ 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
      })
    }
  }
})
DEMO
名前
  • ID.1 スライム HP.100
  • ID.2 ゴブリン HP.200
  • ID.3 ドラゴン HP.500
guide-ch2-demo05

リスト要素を削除しよう

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)
    }
  }
})
DEMO
  • ID.1 スライム HP.100
  • ID.2 ゴブリン HP.200
  • ID.3 ドラゴン HP.500
guide-ch2-demo06

リスト要素プロパティを更新しよう

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を減らす
    }
  }
})
DEMO
  • ID.1 スライム HP.100
  • ID.2 ゴブリン HP.200
  • ID.3 ドラゴン HP.500
guide-ch2-demo07

外部からデータを取得する

83ページ

このサンプルコードでは axios を使用しています。「axios.min.js」を読み込んでください。

list.json
[
  { "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://」 というスキームを使ってブラウザで開いている場合、Ajax での JSON の読み込みはローカルのファイルになってしまうため、セキュリティの問題でエラーになります。 JSON ファイルを適当なホスティングサーバーにアップロードして、「http://」or「https://」で呼び出すか、Chrome なら --allow-file-access-from-files オプションを付けてブラウザを起動してください。

セキュリティ上、このオプションを付けたまま通常のブラウジングはしないでください

お手軽なところだと XAMPP や、Docker の「hello-world-nginx」でコンテナを作成して、localhost から HTML ファイルを開けばオプションなしで読み込むことができます。

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;
}