aws-amplify-vueがカスタマイズできる件

本記事は [CBCloud Advent Calendar](https://qiita.com/advent-calendar/2020/cbcloud) 2020の17日目の投稿です。

---
##環境
・Vue
・IDEA

##目的
  以前、aws-amplifyとaws-amplify-vueを使用して、ログイン機能を実装したのは良いものの、自分好みのフォームを作成したいなと思ったので、今回は、aws-amplify/ui-vueで再実装する。<br> <br>1. **emailとpasswardだけで認証できるように設定する**<br>2. その後、routerで**ログイン済みユーザーだけ**が入れるようにする。

##実装
###はじめに

まず初めに、aws-amplifyとui-vueをインストールしましょう。

```
yarn add aws-amplify @aws-amplify/ui-vue
//or 
npm install aws-amplify @aws-amplify/ui-vue
```

amplifyの設定は、以前したものをそのまま使用しています。

以前amplifyを設定した時に参考にしたサイト
https://day-journal.com/memo/try-024/
###構造
関係ありそうなやつだけ表示しています。

```
├── main.js
├── App.vue
├── router
│   ├── index.js
│   └── route.js
├── components
│   └── pages
│       └── Login.vue
└── store
    └── user.js
```

###各種コード
####main.js
@aws-amplify/ui-vueを入れます。

```js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/user'
import vuetify from './plugins/vuetify'

import '@aws-amplify/ui-vue'
import Amplify, * as AmplifyModules from 'aws-amplify'
import awsconfig from './aws-exports'
Amplify.configure(awsconfig)

Vue.use(AmplifyModules)

Vue.config.productionTip = false
Vue.prototype.$http = axios

new Vue({
  router,
  store,
  vuetify,
  render: h => h(App),
}).$mount('#app')
```

####App.vue
自分はこのしたに、MainTemplateとSubTemplateを入れています。<br>MainTemplateは、主に認証が必要なページ、SubTemplateは、認証の必要がないページです。

```vuejs

<template>
  <div id="app">
    <v-app>
      <v-content>
        <router-view></router-view>
      </v-content>
    </v-app>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>
```
####router/index.js
今回の肝的な部分です。onAuthUIStateChangeで、AuthStateの変更を検知し、検知した場合に、storeに保管します。<br>お馴染みbeforeResolveで、認証が必要なページに入る前に、ログインしているかどうかチェックします。<br>認証がなかった場合には、loginページに飛ばします。

```js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './route'

import store from '../store/user'

import AmplifyModules from 'aws-amplify'

import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components'

Vue.use(VueRouter)
Vue.use(AmplifyModules)

onAuthUIStateChange((nextAuthState, authData) => {
  if (nextAuthState === AuthState.SignedIn) {
    store.commit('setUser', authData)
    router.push({ path: '/' })
  }
  if (!authData) {
    router.push({ path: '/login' })
    store.commit('setUser', null)
  }
})

let user

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
})

router.beforeResolve(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    user = store.state.user
    if (!user) {
      return next({
        path: '/login',
      })
    }
    return next()
  }
  return next()
})

export default router
```
####router/route.js
今回はMainとSubに別れていて、Mainに入る時には、

```js
meta: { requireAuth: true }
```
認証を必要にしています。

```js
export default [
  {
    name: 'MainTemplate',
    path: '/',
    component: () => import('../components/template/MainTemplate'),
    meta: { requiresAuth: true },
    children: [
     //なんか入ってる
    ],
  },
  {
    name: 'SubTemplate',
    path: '/',
    component: () => import('../components/template/SubTemplate'),
    children: [
      {
        name: 'Login',
        path: '/login',
        component: () => import('../components/pages/Login'),
      },
    ],
  },
]
```
####components/pages/Login

Loginページです。username-aliasに"email"を入れることで、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704121/1a772d38-87f5-5d4f-283b-e627357023f9.png" width=40%>-><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704121/0a0b08b4-6a6a-1842-4ddb-73ad3359a458.png" width=40%>
こんな感じになります。
そして、sign-up時のカスタマイズは、formFieldsで設定できます。


```vue
<template>
  <div class="login">
    <amplify-authenticator username-alias="email">
      <amplify-sign-up
        slot="sign-up"
        username-alias="email"
        :form-fields.prop="formFields"
      ></amplify-sign-up>
      <amplify-sign-in slot="sign-in" username-alias="email"></amplify-sign-in>
    </amplify-authenticator>
  </div>
</template>

<script>
import { onAuthUIStateChange } from '@aws-amplify/ui-components'
export default {
  name: 'Login',
  data: () => ({
    user: undefined,
    authState: undefined,
    formFields: [
      {
        type: 'email',
        label: 'Custom email Label',
        placeholder: 'custom email placeholder',
        required: true,
      },
      {
        type: 'password',
        label: 'Custom Password Label',
        placeholder: 'custom password placeholder',
        required: true,
      },
    ],
  }),
  created() {
    onAuthUIStateChange((authState, authData) => {
      this.authState = authState
      this.user = authData
    })
  },
  beforeDestroy() {
    return onAuthUIStateChange
  },
}
</script>
```
####store/user.js
普通のstoreです。

```js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    user: null,
  },
  mutations: {
    // ユーザー情報保存
    setUser(state, user) {
      state.user = user
    }
  },
})

export default store
```

これで動くはずです。

##後述
私が、先日実装したやつなのですが、もっと効率の良い実装方法はあるはずです。
もし何かあれば、教えていただけるとありがたいです。

##参考文献
  カスタマイズ前のコード -> https://day-journal.com/memo/try-024/
  本家 -> https://docs.amplify.aws/ui/q/framework/vue
  amplifyの環境構築 -> https://qiita.com/shunp/items/d491adfadd570f66f990