【Nuxt.js】Modal実践編：QueryでModalを管理する②

# 前置き
![picture_pc_8be74d31bdb9a712682ef9abdc34e5a9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/82cb4cb1-c83e-d052-9831-849e53f29fa1.gif)

前回の続きです🌟
https://note.com/aliz/n/n47b0d98be524

1つのcomponentsで
複数のModalを表示させましょう✨

# Step5: 中身をcomponents分けする

Modalの外側と内側で
componentsを切り分けていきましょう。
queryによって切り替えているpタグを
organismsに移動させます🎈🧸
propsを使わなくて済むので
本当にただ移動させるだけです、楽ちん♪

```bash:file
components/
--| organisms/
----| modals/
-----| ModalContainer.vue
--| templates/
----| modals/
-----| ModalRoute.vue

layouts/
--| default.vue
```

```ModalContainer.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <p
     v-if="$route.query.modal === 'login'"
     class="text"
   >
     {{ $route.query.modal }}
   </p>
   <p
     v-if="$route.query.modal == 'register'"
     class="text"
   >
     {{ $route.query.modal }}
   </p>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .text {
     font-size: 36px;
   }
 }
</style>
```

# Step6: 中身をFormにする

![picture_pc_8be74d31bdb9a712682ef9abdc34e5a9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/5e2c09b0-40f6-7251-7301-5884d6090abb.gif)

ついでにformを作りましょう🌟
password入力のinputも作ると
inputをmoleculesで作る必要が出てくるので
一旦イメージだけできればOKです🧸🍯

```ModalContainer.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     class="form"
     v-if="$route.query.modal === 'login'"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="$emit('submit', form)"
     >
       {{ $route.query.modal }}
     </button>
   </form>
   <form
     class="form"
     v-if="$route.query.modal === 'register'"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="$emit('submit', form)"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

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

**【解説】**
**・@click="$emit('submit', form)"**
　inputに入力する値formを$emitで渡します。

```ModalRoute.vue
<template>
 <div
   v-if="$route.query.modal"
   class="modal-route"
 >
   <div
     class="bg"
     @click="$router.push('/')"
   />
   <div class="modal-wrap">
     <button
       class="button"
       @click="$router.push('/')"
     >
       <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z" /></svg>
     </button>
     <ModalContainer
       @submit="submit"
     />
   </div>
 </div>
</template>

<script>
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 components: {
   ModalContainer: () => import('@/components/organisms/modals/ModalContainer.vue'),
 },
 methods: {
   submit (form) {
     console.log(form) // eslint-disable-line
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-route {
   position: fixed;
   top: 0;
   width: 100%;
   height: 100%;

   .bg {
     width: 100%;
     height: 100%;
     background-color: rgba(0,0,0,0.5);
   }

   .modal-wrap {
     border-radius: 8px;
     background-color: #ffffff;
     width: 50%;
     height: 50%;
     position: absolute;
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
     padding: 30px;

     .button {
       border: none;
       position: absolute;
       top: 5%;
       right: 2%;
     }
   }
 }
</style>
```

**【解説】**
**・＠submit**
　$emitでつけたイベント名
**・// eslint-disable-line**
　consoleなど特定の行に書かないと
　ESLintでエラーになります。
　https://qiita.com/nju33/items/2d0cfea4fffbfdbff87a

# Step7: Formを分ける

今はformが2種類ですが
もし増えたら管理が面倒なので
分けてしまいましょう💡

え、1つのコンポーネントを
queryで切り替えるのが
メリットなんじゃないの？😲
分けたら意味なくない？😲
と思ったそこのアナタ❗️

安心してください🍑
componentタグを使えば良いのです✨

[コンポーネント の基本](https://jp.vuejs.org/v2/guide/components.html?#%E5%8B%95%E7%9A%84%E3%81%AA%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88)

**【ディレクトリ 】**
modalsというファイルを作り
ModalContainerのform2つを
それぞれに分けましょう。

**🌷before🌷**

```bash:file
components/
--| organisms/
----| modals/
-----| ModalContainer.vue
--| templates/
----| modals/
-----| ModalRoute.vue

layouts/
--| default.vue
```

**🌷after🌷**

```bash:file
components/
--| templates/
----| modals/
-----| ModalRoute.vue

modals/
--| login.vue
--| register.vue

layouts/
--| default.vue
```

**【modals/login.vue】**
・formのregister部分を除きましょう🧹
・$emitのイベント名を分かりやすく
　submitLoginに変更しましょう！

```login.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     v-if="$route.query.modal === 'login'"
     class="form"
     @submit.prevent="$emit('submitLogin', form)"
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

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

**【modals/register.vue】**
変更部分はlogin.vueと同じです

```register.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     v-if="$route.query.modal === 'register'"
     class="form"
     @submit.prevent="$emit('submitRegister', form)"
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

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

**【components/organisms/modals/ModalRoute.vue】**

```NidakRoute.vue
<template>
 <div
   v-if="$route.query.modal"
   class="modal-route"
 >
   <div
     class="bg"
     @click="$router.push('/')"
   />
   <div class="modal-wrap">
     <button
       class="button"
       @click="$router.push('/')"
     >
       <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z" /></svg>
     </button>
     <!-- <ModalContainer
       @submit="submit"
     /> -->
     <component
       :is="$route.query.modal"
       @submitLogin="submit($event)"
       @submitRegister="submit($event)"
     />
   </div>
 </div>
</template>

<script>
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 components: {
   login: () => import('@/modals/login.vue'),
   register: () => import('@/modals/register.vue'),
 },
 methods: {
   submit (form) {
     console.log(form) // eslint-disable-line
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-route {
   position: fixed;
   top: 0;
   width: 100%;
   height: 100%;

   .bg {
     width: 100%;
     height: 100%;
     background-color: rgba(0,0,0,0.5);
   }

   .modal-wrap {
     border-radius: 8px;
     background-color: #ffffff;
     width: 50%;
     height: 50%;
     position: absolute;
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
     padding: 30px;

     .button {
       border: none;
       position: absolute;
       top: 5%;
       right: 2%;
     }
   }
 }
</style>
```

**【解説】**
**・component :is="$route.query.modal"**
　└:isでcomponentを呼び出します！
　　コンポーネントの登録が必要なので
　　中身をそれぞれimportしましょう📩
　└それぞれを1つのコンポーネントとみなし
　　queryで切り替えましょう🍒
**・@submitLogin="submit($event)"**
　└$emitのイベント名を変えたので
　　どちらも書きましょう✍️
　└($event)は省略可能

お疲れ様でした🤗
完成です🎉💕

# 次回予告

【Nuxt.js】アプリ開発実践編：
Nuxt + Vuex + firebaseでログイン付きToDoリスト

こちらのTODOリストに
ログイン機能をつけていきます！✨
https://note.com/aliz/n/n8411db2c9a20

公開予定日は5/19(火)です🌟