こんにちは、かつコーチです。
前回は v-on によるイベントハンドリングを扱いました。
今回は、HTML属性やコンポーネントのプロパティを「動的に」切り替える v-bind ディレクティブを解説します。
v-on が「操作を検知する」仕組みなら、v-bind は「データを反映する」仕組みです。
この2つが揃うと、Vueらしい「データに応じて見た目が変わるUI」が一通り作れるようになります。
v-bindとは?
属性を動的に切り替えるディレクティブ
v-bind は、img の src や a の href といったHTML属性の値を、リアクティブなデータと連動させるディレクティブです。
<script setup lang="ts">
import { ref } from 'vue'
const imageUrl = ref<string>('/images/cat.png')
const altText = ref<string>('ねこの写真')
</script>
<template>
<img v-bind:src="imageUrl" v-bind:alt="altText" />
</template>
imageUrl の値が変われば、画面に表示される画像も自動的に切り替わります。
v-on に @ という省略記法があったように、v-bind にも : という省略記法があります。
<template>
<img :src="imageUrl" :alt="altText" />
</template>
実際の開発では、こちらの : を使った書き方が圧倒的に多いです。
通常の属性との違い
普通のHTMLでは、属性値は常に固定の文字列です。
<!-- 常に同じ画像しか表示されない -->
<img src="/images/cat.png" alt="ねこの写真" />
v-bind を使うと、属性値の中にJavaScript(TypeScript)の変数や式を書けるようになります。
これにより、「ログイン状態によってボタンの見た目を変える」「配列の内容によって表示する画像を切り替える」といった動的な表現が可能になります。
基本の書き方
クラスをオブジェクト形式でバインドする
class 属性のバインディングは特に使用頻度が高く、Vueには専用の便利な書き方が用意されています。
<script setup lang="ts">
import { ref } from 'vue'
const isActive = ref<boolean>(true)
const hasError = ref<boolean>(false)
</script>
<template>
<div :class="{ active: isActive, error: hasError }">
ステータス表示
</div>
</template>
オブジェクトのキーがクラス名、値が true のときだけそのクラスが適用されます。
上記の例では isActive が true なので、<div class="active"> として描画されます。
クラスを配列形式でバインドする
複数のクラスを組み合わせたい場合は、配列形式も使えます。
<script setup lang="ts">
import { ref, computed } from 'vue'
const size = ref<'small' | 'medium' | 'large'>('medium')
const sizeClass = computed<string>(() => `btn-${size.value}`)
</script>
<template>
<button :class="['btn', sizeClass]">送信</button>
</template>
computed と組み合わせることで、「サイズの種類」というデータから「クラス名」という文字列を導き出す、という自然な流れで書けます。
styleを動的にバインドする
style もオブジェクト形式でバインドできます。
<script setup lang="ts">
import { ref } from 'vue'
const progress = ref<number>(60)
</script>
<template>
<div class="bar-outer">
<div class="bar-inner" :style="{ width: `${progress}%` }"></div>
</div>
</template>
progress の値に応じてバーの幅が変わる、プログレスバーのような表現が簡単に実現できます。
CSSのプロパティ名はキャメルケース(backgroundColor など)で書ける点も覚えておくと便利です。
よくあるつまずきポイント・エラー対処
v-bindを付け忘れて文字列として表示してしまう
私が最初につまずいたのが、v-bind(:)の付け忘れでした。
❌ Before::を付け忘れて変数名がそのまま文字列になる
<script setup lang="ts">
import { ref } from 'vue'
const imageUrl = ref<string>('/images/cat.png')
</script>
<template>
<!-- srcの値が文字列"imageUrl"として扱われてしまう -->
<img src="imageUrl" alt="ねこ" />
</template>
このコードを実行すると、imageUrl という名前の画像ファイルを探しにいってしまい、画像が表示されずに壊れたアイコンだけが表示されます。
私はこれで「画像が出ない」と数十分悩んだことがあるのですが、原因は単純で :src にすべきところを src と書いていただけでした。
✅ After::(v-bind)を付けて変数の中身を評価させる
<script setup lang="ts">
import { ref } from 'vue'
const imageUrl = ref<string>('/images/cat.png')
</script>
<template>
<img :src="imageUrl" alt="ねこ" />
</template>
: を付けることで、imageUrl は「変数」として評価され、中に入っている文字列(/images/cat.png)が実際の属性値として使われます。
「属性の値を固定文字列にしたいのか、変数の中身にしたいのか」を意識して : の有無を判断する癖をつけると、このミスは防げます。
booleanなHTML属性の扱いを間違える
disabled のような真偽値の属性でも、つまずきやすいポイントがあります。
❌ Before:文字列の”false”を渡してしまい常に無効化される
<script setup lang="ts">
const isDisabled = 'false' // 文字列の"false"(本来はbooleanにすべき)
</script>
<template>
<!-- 文字列"false"はtruthyなので、ボタンは常に無効化されたままになる -->
<button :disabled="isDisabled">送信</button>
</template>
JavaScriptでは空文字以外の文字列は真(truthy)として扱われるため、"false" という文字列も「真」と判定されてしまいます。
その結果、disabled 属性が常に付いた状態になり、ボタンがずっと押せない状態になってしまいます。
✅ After:booleanの値を渡す
<script setup lang="ts">
import { ref } from 'vue'
const isDisabled = ref<boolean>(false)
</script>
<template>
<button :disabled="isDisabled">送信</button>
</template>
TypeScriptで ref<boolean> のように型を明示しておくと、うっかり文字列を代入しようとした時点でエディタが警告を出してくれます。
真偽値を扱う変数は、最初から boolean 型として宣言しておくのが安全です。
応用・一歩先の使い方
複数の属性をまとめてバインドする
オブジェクトにまとめた属性を、一括でバインドすることもできます。
<script setup lang="ts">
interface ImageAttrs {
src: string
alt: string
width: number
}
const imageAttrs: ImageAttrs = {
src: '/images/cat.png',
alt: 'ねこの写真',
width: 300,
}
</script>
<template>
<img v-bind="imageAttrs" />
</template>
v-bind="オブジェクト" の形にすると、オブジェクトのキーがそのまま属性名として展開されます。
APIから取得したデータをそのまま画像属性として渡したい場合など、属性の数が多いケースで重宝します。
まとめ
この記事のポイント
v-bind(省略形:)で属性値をリアクティブなデータと連動させられるclassやstyleはオブジェクト・配列形式で動的に切り替えるのが定番:を付け忘れると、変数名が文字列としてそのまま表示されてしまうdisabledなどのboolean属性には、文字列ではなくboolean型の値を渡すv-bind="オブジェクト"で複数の属性をまとめてバインドできる
次に読むべき記事
次回は、フォームの入力値とデータを双方向に結びつける v-model を解説します。
→ 次の記事:v-modelでフォームの双方向バインディングを実装する