Vue CLI3 で作成した SPA(Single Page Application)プロジェクト上で、段階的に Vue.js を学んで行きましょう。
今回はコンポーネント編です。
TodoList を作りながらコンポーネントの使い方を学びます。
前提事項
Vue の基本 が完了していること。
ページの追加
新規に Todo List ページを作成します。
- TodoList.vue を作成
- router.js を修正
- App.vue にナビゲーションを追加
やり方を忘れてしまった人は「Vue Router 編」を振り返ってください。
<template>
<div class="todolist">
<h1>Todo</h1>
<ul v-if="todos.length">
<li
v-for="todo in todos"
:key="todo.id"
>{{ todo.text }}</li>
</ul>
<p v-else>
TODO 一覧はありません
</p>
</div>
</template>
<script>
export default {
data() {
return {
todos: [
{ id: 1, text: "Learn Vue" },
{ id: 2, text: "Learn about single file components" },
{ id: 3, text: "Learn about components" }
]
};
}
};
</script>
<style>
h1 {
text-align: center;
}
.todolist {
max-width: 400px;
margin: 0 auto;
text-align: left;
}
</style>
コンポーネントの作成とプロパティによる値渡し
Todo List から Todo List Item を別コンポーネントに切り出します。
TodoListItem.vue コンポーネントを作成します。
<template>
<!-- todo プロパティのテキストを表示 -->
<li>{{ todo.text }}</li>
</template>
<script>
export default {
// 親コンポーネントから todo プロパティを受け取る
props: ["todo"]
};
</script>
親コンポーネントから子コンポーネントに値を渡すにはプロパティを使用します。
上記では todo
プロパティを受け取れるよう宣言しています。
TodoList.vue を下記の様に修正します。
<template>
<div class="todolist">
<h1>Todo</h1>
<ul v-if="todos.length">
<!-- TodoListItem コンポーネントを使います -->
<!-- v-bind を使って動的に todo プロパティを渡します -->
<TodoListItem
v-for="todo in todos"
:key="todo.id"
:todo="todo"
/>
</ul>
<p v-else>
TODO 一覧はありません
</p>
</div>
</template>
<script>
// TodoListItem コンポーネントを import します
import TodoListItem from "../components/TodoListItem";
export default {
// 使用するコンポーネントを Vue に伝えます
components: {
TodoListItem
},
data() {
// ...
}
};
</script>
<style>
/* ... */
</style>
JavaScript のところで先程作成したTodoListItem.vue
をインポートして、Vue オブジェクトのcomponents
属性に設定しています。こうすることで、HTML のところで、TodoListItem
タグが使用できるようになります。
HTML のところではli
タグの代わりにTodoListItem
タグを使用するように変更しました。子コンポーネントに値を渡すためにtodo
プロパティにtodo
オブジェクトをバインドしています。
プロパティのバリデーション
プロパティは以下のように記述することでバリデーションチェックをすることができます。
export default {
props: {
// 基本的な型の検査 (`null` と `undefined` は全てのバリデーションにパスします)
propA: Number,
// 複数の型の許容
propB: [String, Number],
// 文字列型を必須で要求する
propC: {
type: String,
required: true
},
// デフォルト値つきの数値型
propD: {
type: Number,
default: 100
},
// デフォルト値つきのオブジェクト型
propE: {
type: Object,
// オブジェクトもしくは配列のデフォルト値は
// 必ずそれを生み出すための関数を返す必要があります。
default: function () {
return { message: 'hello' }
}
},
// カスタマイズしたバリデーション関数
propF: {
validator: function (value) {
// プロパティの値は、必ずいずれかの文字列でなければならない
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
}
TodoListItem.vue
もバリデーションチェックをするよう修正します。
props: {
todo: {
type: Object, // オブジェクト型であること
required: true // 必須項目
}
}
イベント
親コンポーネントから子コンポーネントへはプロパティを使って値を渡すことができますが、子コンポーネントから親コンポーネントへ値を渡すことは出来ません。その代わりにイベントを使って通信することができます。
イベントの発火
TodoListItem.vue
に削除ボタンを設け、remove
イベントを発火するよう修正します。
<li>
{{ todo.text }}
<button @click="$emit('remove', todo.id)">x</button>
</li>
botton
がクリックされると $emit('イベント名', ペイロード)
でイベントを発火します。
イベントハンドリング
TodoList.vue
で remove
イベントをハンドリングして Todo を削除します。
<!-- remove イベントをハンドリングして removeTodo メソッドをコールします -->
<TodoListItem
v-for="todo in todos"
:key="todo.id"
:todo="todo"
@remove="removeTodo"
/>
methods: {
removeTodo(idToRemove) {
this.todos = this.todos.filter(todo => {
return todo.id !== idToRemove;
});
}
}
removeTod
メソッドにはremove
イベントが発火された時のペイロードが引数として渡されます(この場合は todo の id です)。
演習
TodoInputText.vue
コンポーネントを作成して、TodoList.vue
でそれを使って TODO を追加できるようにしてください。