モーダルウィンドウから学ぶVue.js

# はじめに
去年の今頃に僕はVue.jsに出会った。
今日はフロントエンドでは絶対聞いたことある「モーダルウィンドウ」を踏まえて、Vue.jsのおさらいをしていきます。

※参考書：「基礎から学ぶVue.js(通称：猫本)」と「Vue.js入門」は大変わかりやすいので理解が深まりました。

# Vue.js文法
モーダルウィンドウで使うであろうVue.jsの文法。
（モーダルウィンドウ以外でももちろん使います。）

## transitionタグ
transitionはCSSアニメーションをより簡単に使いやすくする機能。
[参考：Enter/Leave とトランジション一覧](https://jp.vuejs.org/v2/guide/transitions.html)

```HTML
<transition>
     <div>トランジションさせたい要素</div>
</transition>
```

上記のタグで設定完了です。
この囲んだ要素がDOMに追加される場合は```enter```、削除される場合は```leave```というクラスがトランジションに追加されます。

フェードインとフェードアウトの中で付与されるトランジションのクラスは６種類です。

[参考：トランジションクラス](https://jp.vuejs.org/v2/guide/transitions.html#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%AF%E3%83%A9%E3%82%B9)
> ```v-enter```: ```enter``` の開始状態。要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除されます。
```v-enter-active```: ```enter``` の活性状態。トランジションに入るフェーズ中に適用されます。要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの開始に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
```v-enter-to```: バージョン 2.1.8 以降でのみ利用可能です。 ```enter``` の終了状態です。要素が挿入後 (同時に v-enter が削除されます) 1 フレームが追加されます。トランジション/アニメーションが終了すると削除されます。
```v-leave```: ```leave``` の開始状態。トランジションの終了がトリガされるとき、直ちに追加され、1フレーム後に削除されます。
```v-leave-active```: ```leave``` の活性状態。トランジションが終わるフェーズ中に適用されます。```leave``` トランジションがトリガされるとき、直ちに追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの終了に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
```v-leave-to```: バージョン 2.1.8 以降でのみ利用可能です。 ```leave``` の終了状態です。トランジションの終了がトリガされた後 (同時に v-leave が削除されます) 1 フレームが追加されます。トランジション/アニメーションが終了すると削除されます。

## slotタグ
スロットタグは親となるコンポーネントから、子となるコンポーネントのテンプレートの一部に差し込む機能のことです。テンプレートの一部の機能を親要素に定義されている機能に差し変わります。

## その他
```$emit('aaa')``` ... 親要素のmethods：```aaa```が呼び出されます。
```.self``` ... ```event.target```要素が自分自身のときにハンドラが呼び出されます。

```HTML
<div class="modal modal-overlay" @click.self="$emit('close')">
```

# サンプルコード

```vuejs:app.vue
<template>
    <div class="example-modal-window">
        <p>modalWindow open</p>
        <button @click="openModal">open</button>

        <!-- 以下モーダルウィンドウのコンポーネントを定義(ModalWindow.vue) -->
        <MyModal v-if="modal" @close="closeModal">
            <p>Vue.js Modal Window!!</p>
            <div>
                <input type="text" v-model="message">
            </div>
            <!-- footerスロットコンテンツ -->
            <template slot="footer">
                <button @click="doSend">send</button>
            </template>
        </MyModal>
    </div>
</template>

<script>
import MyModal from 'MyModal.vue'
export default {
    components: {
        MyModal
    },
    data() {
        return {
            modal: false,
            message: ''
        }
    },
    methods: {
        openModal: () => {
            this.modal = true
        },
        closeModal: () => {
            this.modal = false
        },
        doSend: () => {
            if(this.message.length > 0){
                alert(this.message)
                this.message = ''
                this.closeModal()
            }else{
                alert("message is not valid")
            }
        }
    }
}
</script>
```

```vuejs:ModalWindow.vue
<template>
    <!-- ノードの初期描画時にトランジションを適用したい場合はappear -->
    <transition name="modal" appear>
        <div class="modal modal-overlay" @click.self="$emit('close')">
            <div class="modal-window">
                <div class="modal-content">
                    <slot/>
                </div>
                <footer>
                    <slot name="footer"><!-- slot="footer"が親要素のコンポーネントに置き換わる -->
                        <button @click="$emit('close')">Close</button>
                    </slot>
                </footer>
            </div>
        </div>
    </transition>
</template>

<style lang="scss" scoped>
.modal{
    &.modal-overlay {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        z-index: 30;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0,0,0,0.5);
    }

    &-window{
        background: #fff;
        border-radius: 4px;
        overflow: hidden;
    }

    &-content {
        padding: 10px 20px;
    }

    &-footer {
        background: #ccc;
        padding: 10px;
        text-align: right;
    }
}

.modal-enter-active, .modal-leave-active {
    transition: opacity 0.4s;
    .modal-window {
        transition: opacity 0.4s, transform 0.4s;
    }
}

.modal-leave-active {
    transition: opacity 0.6s ease 0.4s;
}

.modal-enter, .modal-leave-to {
    opacity: 0;

    .modal-window {
        opacity: 0;
        transform: translateY(-20px);
    }
}
</style>
```

# 解説
## app.vue
まず、ModalWindowコンポーネントを定義します。
その際に```v-if(boolean:modal)```を使用してモーダルウィンドウの表示/非表示を制御しています。openをクリックすると、モーダルウィンドウが表示されます。

※```template slot=""```を使用すると複数の要素をグループ化できます。

## ModalWindow.vue
ここでは```script```タグはありません。親要素(app.vue)で定義されたmethodsを使って、動的コンテンツを作成します。```$emit()```を用いて親要素のmethods を呼びます。```template slot="footer"```の部分が```footer```タグに差し変わります。

# まとめ
CSSアニメーション機能を使用する場合はトランジションタグ便利ですね。
他にもトランジションタグならではのmethods(BeforeEnter関数)とかも今後紹介していきます。