【Vue.js】タスク詳細ページをモーダルで表示する
はじめに
タスクの詳細ページをモーダルで表示する。
前提
前提として下記のtaskテーブルがあります。
class Task < ApplicationRecord validates :title, presence: true validates :description, length: { maximum: 1000 } end
create_table "tasks", force: :cascade do |t| t.string "title", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.text "description" end
詳細ページのコンポーネントを作成
まずは詳細ページのコンポーネントを作成します。
$ mkdir app/javascript/pages/task $ mkdir app/javascript/pages/task/components $ touch app/javascript/pages/task/components/TaskDetailModal.vue
モーダルはbootstrapのモーダルをベースにします。
<template> <div :id="'task-detail-modal-' + task.id"> <div class="modal" @click.self="handleCloseModal">// :1 <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">{{ task.title }}</h5> <button type="button" class="close" @click="handleCloseModal">// :2 <span>×</span> </button> </div> <div class="modal-body" v-if="task.description"> <p>{{ task.description }}</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" @click="handleCloseModal">閉じる</button>// :2 </div> </div> </div> </div> <div class="modal-backdrop show"></div> </div> </template> <script> export default { name: "TaskDetailModal", props: { // :3 task: { title: { type: String, required: true }, description: { type: String, required: true } } }, methods: { handleCloseModal() { this.$emit('close-modal') // :4 } } } </script> <style scoped> .modal { display: block; // モーダルの表示・非表示をvueでコントールするので最初から表示状態にする } </style>
:1
<div class="modal" @click.self="handleCloseModal">
モーダル以外の部分をクリックしたときにもモーダルを非表示にします。
:2
<button type="button" class="close" @click="handleCloseModal"> <button type="button" class="btn btn-secondary" @click="handleCloseModal">閉じる</button>
モーダルのxボタンと閉じるボタンをクリックした時にモーダルを非表示にします。
:3
props: { task: { title: { type: String, // データのタイプを指定 required: true // 必須かどうか }, description: { type: String, required: true } }
props
を使って親コンポーネントからデータを受け取ります。
:4
methods: { handleCloseModal() { this.$emit('close-modal') } }
$emit
メソッドを使って親コンポーネントのイベントを発火します。
詳細ページのモーダルを呼び出す
<template> <div> <div class="d-flex"> <div class="col-4 bg-light rounded shadow m-3 p-3"> <div class="h4">TODO</div> <div v-for="task in tasks" :key="task.id" :id="'task-' + task.id" class="bg-white border shadow-sm rounded my-2 p-4" @click="handleShowTaskDetailModal(task)">// :1 <span>{{ task.title }}</span> </div> </div> </div> <div class="text-center"> <router-link :to="{ name: 'TopIndex' }" class="btn btn-dark mt-5">戻る</router-link> </div> <transition name="fade"> // モーダルの表示非表示にtrannsitionをつける。 <TaskDetailModal v-if="isVisibleTaskDetailModal" @close-modal="handleCloseTaskDetailModal" :task="taskDetail" /> // :2 </transition> </div> </template> <script> import TaskDetailModal from './components/TaskDetailModal' // 詳細ページのコンポーネントをインポート export default { components: { TaskDetailModal }, name: "TaskIndex", data() { return { tasks: [], taskDetail: {}, isVisibleTaskDetailModal: false // モーダルをデフォルトで非表示に設定 } }, created() { this.fetchTasks(); }, methods: { fetchTasks() { this.$axios.get("tasks") .then(res => this.tasks = res.data) .catch(err => console.log(err.status)); }, handleCloseTaskDetailModal() { // 子コンポーネントから呼び出しているメソッド this.isVisibleTaskDetailModal = false; // モーダルを非表示にする this.taskDetail = {}; // 引数のタスクを空にする }, handleShowTaskDetailModal(task) { this.isVisibleTaskDetailModal = true; // モーダルを表示 this.taskDetail = task; // 引数のタスクを代入 } } } </script> // :4 <style scoped> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
:1
@click="handleShowTaskDetailModal(task)">
クリックされたtaskのデータを引数で渡します。
:2
<TaskDetailModal v-if="isVisibleTaskDetailModal" @close-modal="handleCloseTaskDetailModal" :task="taskDetail" />
v-if
を使ってisVisibleTaskDetailModal
がtrueのときは表示、falseのときは非表示にします。
先程の子コンポーネントから呼んでるイベントが@close-modal
。
(v-bind):task="taskDetail"
でtaskのデータを子コンポーネントに渡しています。
そして子コンポーネントがprops
で受け取っています。
:3
下記4つのクラスはenter/leave
トランジションを適用するための特殊なクラスです。
fade
の部分はname属性です。
name属性(fadeなど) + 特殊クラス(-enter-activeなど)
<style scoped> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
.fade-enter-active
はenterトランジションの活性状態に適用されます。
今回だとモーダルが表示されるトランジションの開始から終了まで。
.fade-leave-active
はleaveトランジションの活性状態に適用されます。
今回だとモーダルが非表示されるトランジションの開始から終了まで。
完成画面