Chapter 04

属性バインディング

OJK
OJK
2021.08.24に更新

今回は HTML の属性値を petite-vue から変更する v-bind ディレクティブについて学びます。よく使用する class 属性と style 属性のためには特別な記法が用意されています。

雛形コード
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>petite-vue入門</title>
</head>
<body>

  <p>Petite Vue!!</p>

  <script src="https://unpkg.com/petite-vue"></script>
  <script src="script.js"></script>
</body>
</html>
script.js
'use strict';

PetiteVue.createApp({

}).mount();

v-binding ディレクティブ

口ひげ構文 {{ }} は HTML 要素の開始タグと終了タグの間でしか使えませんでしたが、v-binding ディレクティブ を使用すると HTML 要素の属性値に JavaScript の式を割り当てることができます。

なお、「ディレクティブ」という言葉が前回から続いているので気づいたかと思いますが、「v-***」という Vue 専用の HTML 属性のことを “ディレクティブ/Directive” と呼びます。本講座では v-on と v-bind の他にあと 3 つ登場しますが、petite-vue/Vue にはもっとたくさん用意されています。

v-bind の構文は次のとおりです。

v-bindディレクティブ
<HTML要素名 v-bind:属性名="">

そして早速ですが、v-bind もよく使われるディレクティブなので省略形があって、「v-bind:」の代わりに「:」と書くことができます。以降、本講座では省略形を使っていきます。

v-bindディレクティブ(省略形)
<HTML要素名 :属性名="">

具体例を見てみましょう。

HTML
<p><a :href="url">Petite Vue!!</a></p>
JS
PetiteVue.createApp({
  url: 'https://github.com/vuejs/petite-vue',
}).mount();

一見、a 要素の href 属性には "url" という文字列が指定してあるだけのように見えますが、「:href」というように v-bind ディレクティブが適用されているので、"url" は文字列ではなくデータプロパティの url と解釈されます。データプロパティ url には JavaScript 側で有効な URL が設定されているのでリンクはきちんと動作します。

HTML の属性値が「単なる文字列」なのか「JavaScript の式」なのかが見分けづらいというのは petite-vue(Vue)の問題点だと思います。特に v-bind を省略したときは「:」を見落としやすいので、慣れるまではあえて領略せずに「v-bind:」とするほうがよいかもしれません。

href 属性の他にも、ほとんどあらゆる属性値に v-bind は適用できます。

<p :id="p_id">
<img :src="imgFile">
<input type="radio" :name="rName">

v-on と組み合わせるとインタラクティブに変化するウェブページが簡単に作れます。
好きな画像を用意して index.html と同じフォルダに置き、以下のサンプルコードを実行してみてください。画像ファイル名は適宜設定してくださいね。

HTML
<button @click="imgFile = 'bird.jpg'">表示</button>
<img :src="imgFile">
JS
PetiteVue.createApp({
  imgFile: ''
}).mount();

なお、この例ではコールバック処理を v-on に直接記述してみました。データプロパティももちろん使えます。HTML 側での記述なので imgFile に this を付ける必要はありません。

class のバインディング

class 属性に対して、以下のように普通に v-bind を適用することはできます。

CSS
.base { padding: 5px; width: 5em; text-align: center }
.bg1 { background-color: coral }
.bg2 { background-color: cornflowerblue }
HTML
<p class="base" :class="vueClass">クラス</p>
<!-- 普通の class 属性と v-bind:class の混在も可 -->
<button @click="change">切替</button>
JS
PetiteVue.createApp({
  vueClass: 'bg1',
  change() {
    this.vueClass = 'bg2';
  }
}).mount();

しかし、これだと素の JavaScript の classList のほうが扱いやすいですよね。

そこで petite-vue でも v-bind:class の値にオブジェクトを使えるようにして、やや使いやすくなっています。以下のように class 名をプロパティ名とし、それを有効にするか否かをブール値(true/false)で指定します。

データプロパティ名: {
  class名: ブール値,
  class名: ブール値,
  ...
}

さきほどの例を書き換えてみましょう。

HTML
<p class="base" :class="classObj">クラス</p>
<button @click="change">切替</button>
JS
PetiteVue.createApp({
  classObj: { bg1: true, bg2: false },
  change() {
    this.classObj.bg1 = false;
    this.classObj.bg2 = true;
  }
}).mount();

単にコードが長くなっただけに感じるかもしれませんが、class 名の文字列がコードのあちらこちらに登場しなくなるだけで価値はあります。また、class の有効/無効がブール値になったことでトグルなどはシンプルに書けます。

JS
PetiteVue.createApp({
  classObj: { bg1: true },
  change() {
    this.classObj.bg1 = !this.classObj.bg1;
    // ブール値はNOT演算子 ! を付けて代入すると反転する
  }
}).mount();

style のバインディング

v-bind:style の値もオブジェクトで記述することができます。こちらは普通の CSS の記述によく似た形式になります。

データプロパティ名: {
  CSSプロパティ名: '値',
  CSSプロパティ名: '値',
  ...
}

通常の CSS の記法とは次の 3 点で異なるので混同しないよう注意してください。

  • CSS プロパティ名はキャメルケースで記述する[1]
  • プロパティ値は引用符で囲う必要がある(数値のみの場合は不要)
  • 区切り文字がセミコロンではなくコンマになる

具体例を見てみましょう。

HTML
<p :style="styleObj">スタイル</p>
JS
PetiteVue.createApp({
  styleObj: {
    width: '6em',
    padding: '10px',
    border: 'solid 2px firebrick',
    textAlign: 'center' // キャメルケース
  }
}).mount();

この例では、styleObj というオブジェクトをデータプロパティとして定義して、それを HTML 側で v-bind:style に設定しています。「text-align」がキャメルケースで「textAlign」になっていますね。

メソッドからデータプロパティ styleObj を変更すると HTML にも即座に反映されます。ボタンを押したらスタイルが変わるようにしてみましょう。

HTML
<p :style="styleObj">スタイル</p>
<button @click="change">変更</button>
JS
PetiteVue.createApp({
  styleObj: { /*略*/ },
  change() {
    this.styleObj.color = 'firebrick';
    this.styleObj.borderWidth = '5px';
  }
}).mount();

petite-vue のメソッド定義からは this を付けることでデータプロパティにアクセスできます。この例では、文字色(color)を新たに設定し、既存の border から太さのみ変更しています。

配列による class/style の指定

class 属名と style 属性には配列で値を指定することも可能です。

HTML
<p :style="[styleObj1, styleObj2]">スタイル</p>
JS
PetiteVue.createApp({
  styleObj1: { border: 'solid 2px royalblue' },
  styleObj2: { color: 'firebrick' }
}).mount();

上記のように直接配列で指定してもあまりうまみはありませんが、データプロパティを介することで配列のインデックスを使ったスタイルの切り替えが可能になります。

HTML
<p :style="styleList[idx]">スタイル切り替え!</p>
<button @click="change(0)">Style 1</button>
<button @click="change(1)">Style 2</button>
JS
PetiteVue.createApp({
  styleList: [
    { border: 'solid 2px royalblue' },
    { color: 'firebrick' }
  ],
  idx : 0,
  change(n) {
    this.idx = n;
  }
}).mount();

ここで、スタイルオブジェクトを一旦データプロパティに定義してから styleList に追加したほうが、スタイルオブジェクトの再利用ができてよい…と思ったかもしれません。

不可な例
styleList: [styleObj1, styleObj2], // 不可
styleObj1: { border: 'solid 2px royalblue' },
styleObj2: { color: 'firebrick' }

しかし残念ながら、petite-vue ではこのような書き方はできません。これは petite-vue ではなく JavaScript の仕様で、オブジェクト内のプロパティ定義で他のプロパティは使用できないのです。

データプロパティに他のプロパティの値を指定したいときは、ゲッターを使うとよいでしょう。

ゲッターを使った例
PetiteVue.createApp({
  // データプロパティ
  styleObj1: { border: 'solid 2px royalblue' },
  styleObj2: { color: 'firebrick' },
  idx: 0,

  // ゲッター
  get styleList() {
    return [this.styleObj1, this.styleObj2];
  },

  // メソッド
  change(n) {
    this.idx = n;
  }
}).mount();
脚注
  1. CSS プロパティにはケバブケースも使えるのですが、キャメルケースとケバブケースを混在させると Vue では反応しないことがあるので、キャメルケースで統一するのがよいでしょう。 ↩︎