SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

TypeScriptで学ぶJavaScriptフレームワーク「Vue.js」の利用法

Vue.jsでWebページをつくる際の肝!「コンポーネント」をTypeScriptで活用しよう

TypeScriptで学ぶJavaScriptフレームワーク「Vue.js」の利用法 第6回


  • X ポスト
  • このエントリーをはてなブックマークに追加

コンポーネントで双方向データバインディングを実装

 propsとemitを組み合わせることで、コンポーネントに対して双方向データバインディングを設定できます。図3のサンプルでは、子コンポーネント内のテキストボックスと、子コンポーネント外のテキストボックスの内容が同期して変更されます。

図3 双方向データバインディングのサンプル(p003-binding)
図3 双方向データバインディングのサンプル(p003-binding)

 親コンポーネントには、PhoneFormコンポーネントをリスト6の通り記述します。

[リスト6]図3の親コンポーネントに記述するPhoneFormコンポーネント(p003-binding/src/App.vue)
<PhoneForm v-model="paramName" v-model:vendor="paramVendor"
  v-model:price="paramPrice"></PhoneForm>

 (1)で、PhoneFormコンポーネントの引数にv-modelディレクティブで変数をバインディングしています。ディレクティブには「v-model」のほか、「v-model:vendor」「v-model:price」の通りプロパティ名を指定することもできます。

 対応する子コンポーネントはリスト7の通りです。

[リスト7]図3の子コンポーネント(p003-binding/src/components/PhoneForm.vue)
<template>
  <!-- inputタグだけ抽出して記述 ...(1)-->
  <input type="text" :value="modelValue" @input="onUpdateModelValue">
  <input type="text" :value="vendor" @input="onUpdateVendor">
  <input type="number" :value="price" @input="onUpdatePrice">
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  props: { // ...(2)
    modelValue: String, // v-modelで指定した名前
    vendor: String,   // v-model:vendorで指定したメーカー
    price: Number   // v-model:priceで指定した価格
  },
  emits: [ // ...(3)
    'update:modelValue',
    'update:vendor',
    'update:price'
  ],
  setup(props, context) {
    const onUpdateModelValue = ((e: Event) => { // ...(4)
      if (e.target instanceof HTMLInputElement) {
        context.emit('update:modelValue', e.target.value)
      }
    })
    (以下略:vendor、priceのイベントハンドラー)
  }
})

 v-modelディレクティブで指定されるプロパティは(2)のpropsに記述します。「v-model:vendor」や「v-model:price」のように、コロンの後ろに名前が指定されたディレクティブに対応するプロパティはその名前になります(「vendor」、「price」)。名前が指定されない「v-model」ディレクティブに対応するプロパティは「modelValue」になる決まりです。同様にemits(3)に指定するイベント名も、「v-model:vendor」「v-model:price」に対応するイベント名はそれぞれ「update:vendor」「update:price」、「v-model」に対応するイベント名は「update:modelValue」となります。

 コンポーネントのinputタグ(1)では、(2)のプロパティを:value(v-bind:valueの省略記法)ディレクティブに指定してデータを反映するとともに、@input(v-on:inputの省略記法)ディレクティブに(4)のイベントハンドラーを指定して、文字入力時に(3)のイベントをemitすることで、双方向データバインディングの動作が実現します。

<script setup>で記述を簡略化

 これまでの記述は、<script setup>を利用してさらに簡略化できます。以下ではポイントだけを説明します。全てのコードはp002a-emit-setupサンプルを参照してください。

参照するコンポーネントの指定

 <script setup>を利用しない場合、参照するコンポーネントをdefineComponentのcomponentsに設定していました。<script setup>を利用する場合、コンポーネントをインポートするだけで、そのコンポーネントが利用できるようになります。

[リスト8]<script setup>利用時、参照するコンポーネントを指定する記述(p002a-emit-setup/src/App.vue)
<script setup lang="ts">
// importするだけでコンポーネントが利用できる
import PhoneWithBuy from "./components/PhoneWithBuy.vue"
(略)
</script>

プロパティ、emitの指定

 <script setup>を利用しない場合、プロパティやemitは、defineComponentのprops、emitで指定していました。<script setup>を利用する場合、プロパティはリスト9(1)のdefinePropsメソッド、emitは(2)のdefineEmitsメソッドで取得して、(3)の通り利用できます。

[リスト9]<script setup>利用時のprops、emitの記述(p002a-emit-setup/src/component/PhoneWithBuy.vue)
const props = defineProps({ // ...(1)
  phoneData: PhoneData  // PhoneData型プロパティ
})
const emit = defineEmits([ // ...(2)
  'onBuy' // emitする可能性があるイベントを指定
])
const onClickBuyButton = (() => {
  emit('onBuy', props.phoneData) // ...(3)
})

コンポーネント内部のHTML要素を指定するスロット

 最後に、コンポーネントタグの内側に記述された内容を画面に反映する「スロット」を、タイトルと内容の文言からなるエラー表示を行う図4のサンプルで説明します。

図4 スロットの利用例(p004-slot)
図4 スロットの利用例(p004-slot)

 図4の内容を表示するCustomErrorコンポーネントは、リスト10の通りです。スロットには名前なしのもの(1)と名前付きのもの(2)があります。

[リスト10]図4の子コンポーネント(p004-slot/src/component/CustomError.vue)
<template>
  <div class="error">
    <div class="title">
      <slot></slot> <!-- 名前なしスロット ...(1)-->
    </div>
    <div class="description">
      <slot name="desc"></slot> <!-- 名前付きスロット ...(2)-->
    </div>
  </div>
</template>

 コンポーネントを利用する側の実装は、リスト11の通りです。<CustomError>の内側で、名前がついていない(1)がリスト10(1)の名前なしスロットに、v-slotで名前(desc)がついている(2)の内容がリスト10(2)の名前付きスロットに設定されます。

[リスト11]図4の親コンポーネント(p004-slot/src/App.vue)
<template>
  <CustomError>
    名前なしスロットに渡されます。 <!--(1)-->
    <template v-slot:desc> <!--(2)-->
      名前付きスロットに渡されます。
    </template>
  </CustomError>
</template>

まとめ

 本記事では、Vue.jsでWebページを構成するコンポーネントの記述・利用法を説明しました。Webページを小さいコンポーネントに分割して、プロパティでコンポーネントにデータを設定したり、emitでコンポーネントからイベントを発行したりできます。

 次回は、複数のページを切り替え表示するVue.jsのルーティングについて説明します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
TypeScriptで学ぶJavaScriptフレームワーク「Vue.js」の利用法連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト  吉川 英一(ヨシカワ エイイチ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/16252 2022/08/03 17:12

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング