タスクをモーダルで表示する / 初めてのVue.js⑤

前回の続き　→ https://qiita.com/mmaumtjgj/items/7e9414c33279fa2254fe

タスクモーダルは、親コンポーネント``app/javascript/pages/task/index.vue``から呼ばれるようにしたいので、子コンポーネントとして``javascript/pages/task/components/TaskModal.vue``にて記述する。

タスクモーダルのデザインは[Modal](https://getbootstrap.jp/docs/4.2/components/modal/)を参考に作成（BoostrapのJSは不使用でいく）。

## 子コンポーネント(TaskModal.vue)
```javascript/pages/task/components/TaskModal.vue
<template>
    <div :id="task.id">
        <div class="modal" @click.self="handleCloseModal">
            <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">
                            <span>&times;</span>
                        </button>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" @click="handleCloseModal">閉じる</button>
                    </div>
                </div>
            </div>
        </div>
        <div class="modal-backdrop show"></div>
    </div>
</template>

<script>
export default {
    name: "TaskModal",
    props: {
        task: {
            title: {
                type: String,
                required: true
            }
        }
    },
    methods: {
        handleCloseModal() {
            this.$emit('close-modal')
        }
    }
}
</script>

<style scoped>
.modal {
    display: block;
}
</style>
```

### ``@click.self="handleCloseModal"``
・**``@``**
``@``は``v-on``の省略形。
Vue.js では、``v-on``を用いることでメソッドをイベントリスナーとしてバインドする。

・**``self``**
``self``はイベントが要素自身にアクションした場合のみ発火するようになる。
``self``の実際の動きは[この記事](https://b1san-blog.com/post/vue/vue-event/#self)にてわかりやすいのが載っている。

``self``のようなイベント修飾子一覧はこちら[イベント修飾子一覧](https://qiita.com/Yorinton/items/f7eb54f05609750da7f5#stop)

### ``props``
``props``とは親コンポーネント　→　子コンポーネントに、文字列・数値・配列やオブジェクトなどの値を渡すことができること。

#### propsの使い方
・子のscriptタグ内（js記述部分）のpropsで、親から受け取るプロパティ名を列挙
・親のtemplateタグ内（html記述部分）で、子の呼び出しとデータの受け渡し

```子コンポーネント.vue
<script>
export default {
  props: ['hoge1', 'hoge2']
}
</script>

上記は、propsの名前のみを指定できる。
以下のように書くことで、他の項目も定義できる。

<script>
export default {
  props: {
    hoge1: {
      type: String,
      default: 'abc',
      required: true
    },
    hoge2: {
      type: String,
      default: 'def',
      required: false
    }
  }
}
</script>
```
``type``でデータの型、``default``でデフォルト値、``required``で必須を指定できる。

```親コンポーネント.vue
<template>
  <!-- :(prop名)="(データ名)" -->
  <!-- :hoge1 ⇨ v-bind:hoge1の省略形 -->
  <Child :hoge1="dat1" :hoge2="dat2">
</template>

<script>
import Child from 'child'

export default {
  components: {
    Child
  },
  data () {
    return {
      dat1: 'ABC',
      dat2: 'DEF'
    }
  }
}
</script>
```

### emit
``emit``とは、子コンポーネントで、ユーザーが行ったアクションを親コンポーネントに伝えるときに使う。

#### emitの使い方
・子コンポーネントで``$emit(イベント名)``で値を送信。（イベント名は好きなものでOK）
・親コンポーネントで``v-on:イベント名(@:イベント名)``で発火。

## 親コンポーネント（index.vue）
```javascript/pages/task/index.vue
<template>
    <div id="app" class="d-flex flex-column min-vh-100">
        <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.id"
                    class="bg-white border shadow-sm rounded my-2 p-4"
                    @click="handleShowTaskModal(task)"
                >
                    <span>{{ task.title }}</span>
                </div>
            </div>
        </div>
        <div class="text-center">
            <router-link to="/"><button class="btn btn-dark mt-5 router-link-active">戻る</button></router-link>
        </div>
        <transition name="fade">
            <TaskModal
                v-if="isVisibleTaskModal" 
                @close-modal="handleCloseTaskModal"
            />
        </transition>
    </div>
</template>

<script>
import TaskModal from './components/TaskModal.vue'

export default {
    name: "TaskIndex",
    components: { TaskModal },
    data() {
        return {
            tasks: [],
            isVisibleTaskModal: false
        }
    },
    created() {
    this.fetchTasks();
    },
    methods: {
        fetchTasks() {
            this.$axios.get("tasks")
            .then(res => this.tasks = res.data) 
            .catch(err => console.log(err.status));
        },
        handleShowTaskModal(task) {
            this.isVisibleTaskModal = true;
        },
        handleCloseTaskModal() {
            this.isVisibleTaskModal = false;
        }
    }
}
</script>

<style scoped>
.fade-enter-active, .fade-leave-active {
    transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
    opacity: 0;
}
</style>
```
### ``@click="handleShowTaskModal(task)"``
``<script>``内の``method``にある``handleShowTaskModal``を呼び出している。``(task)``と引数と付けることにより各タスクを呼び出すことができる。

### ``<transition name="fade">``
Vueの``transition``とは、主に「追加した時に、ふわっとフェードインさせたり」、 あるいは「削除した時に、ふわっとフェードアウトさせたり」する時に使うもの」。

アニメーション全般に使えるタグでは無く、DOMの追加と削除の描画に特化したタグ。
このクラスにCSSでスタイルを定義することがアニメーションをつけることになる。

| クラス | 状態 |
|:-:|:-:|
| v-enter  |  enterの開始状態 |
| v-enter-active |  enterのアクティブ状態 |
| v-enter-to |  enterの終了状態 |
|v-leave   |  leaveの開始状態 |
|  v-leave-active |  leaveのアクティブ状態 |
|  v-leave-to | leaveの終了状態  |

下記は[Vue公式](https://jp.vuejs.org/v2/guide/transitions.html#%E5%8D%98%E4%B8%80%E8%A6%81%E7%B4%A0-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3)を参考しにしたものでフワッとしたアニメーションを作成。
```vue
<style scoped>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}

/* .fade-leave-active below version 2.1.8 */
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>
```

### ``<TaskModal ~~/>``
先程作成した子コンポーネントである``TaskModal.vue``。ここでタスクモーダルを呼び出している。

### ``isVisibleTaskModal``
モーダルは最初から画面に出すわけではないので、``false``にしている。
```vue
<script>
data() {
        return {
            tasks: [],
            isVisibleTaskDetailModal: false
        }
</script>
```

### ``@close-modal="handleCloseTaskModal"``
``@close-modal``も先に作った子コンポーネントである``TaskModal.vue``に記載してある``emit``を受け取っている場所。
```vue
<script>
methods: {
        handleCloseModal() {
            this.$emit('close-modal')
        }
</script>
```

## 参考記事
[【Vue.js】イベントハンドリング](https://b1san-blog.com/post/vue/vue-event/)
[Vueのpropsの使い方](https://qiita.com/minuro/items/fa3ddd70ace5b99a1f90)
[【ビギナー脱却】Vue.js これであなたもPropsマスター](https://reffect.co.jp/vue/vue-js-master-props-for-beginner#Props-2)
[【Vue.js】propsの書き方・使い方について解説](https://code-database.com/knowledges/47)
[【Vue.js】propsと$emitの基本の使い方を具体例を交えて解説](https://shimablogs.com/vue-props-emit)
[Vue.js の transition ってなに？](https://python.ms/transition/#_1-%E5%8B%98%E9%81%95%E3%81%84%E3%81%97%E3%81%A6%E3%81%84%E3%81%9F%E3%81%93%E3%81%A8)
[Vue.jsについての基礎（transitionによるアニメーション）](https://qiita.com/watataku8911/items/f691a2999c3e2600173b)
[propsと$emitでデータを引き渡す](https://qiita.com/d0ne1s/items/f88ecd6aaa90c7bbc5d4)