[nuxt.js その４] ページとコンポーネントの作成（ログイン）

# ログインフォームコンポーネントの作成
ログインフォームはこのプロジェクトの中では何度も使うものではないかもしれませんが、今後他のアプリ作成時含めて何度も作るのは面倒なので、コンポーネント化して使いまわせるようにしておきます。

コンポーネントを使う上でひとまず知りたいのは、使い方とデータの受け渡し方法かな（？）

参考：[Vue.jsでコンポーネント親子間の値の受け渡し](https://qiita.com/y_sasaki/items/5bbed5439fcfef9f8c40)

使い方については参考をそのまま真似るとして、データ受け渡しの方法については以下のイメージ

親→子：プロパティによる受け渡し
子→親：$emitの第２引数以降で受け渡し

子から親の変数を書き換えるって方法もあったけど、勝手に親の持つ変数を書き換えてしまうのは気持ち悪いので$emitを採用。
きっとユースケースにより使い分けるんだろうけど。

細かいvueページの書き方については割愛。
出来上がったコンポーネントのソースは以下。

```vuejs:~/components/LoginForm.vue
<template>
  <v-layout
    justify-center
  >
    <v-flex
      xs12
      sm8
      md6
    >
      <v-card
        color="blue-grey darken-2"
      >
        <v-layout column wrap>
          <v-flex pt-3 pb-3 pl-5 pr-5 ma-3>
            <v-card-title class="headline">
              ログイン画面
            </v-card-title>

            <v-divider></v-divider>
            <v-text-field
              v-model="username"
              label="E-mail Address"
              required
            >
            </v-text-field>
            <v-text-field
              :append-icon="visible ? 'visibility' : 'visibility_off'"
              :type="visible ? 'text' : 'password'"
              name="input-10-2"
              label="Password"
              v-model="password"
              @click:append="visible = !visible"
            ></v-text-field>
            <v-layout
              align-center
              justify-center
            >
              <v-btn 
                outline
                color="primary"
                @click="loginAction"
              >
                login
              </v-btn>
            </v-layout>

          </v-flex>
        </v-layout>
      </v-card>
    </v-flex>
  </v-layout>
</template>
<script>
import firebase from '~/plugins/firebase.js'
export default {
  data () {
    return {
      username: '',
      password: '',
      visible: false
    }

  },
  methods: {
    loginAction () {
      if (this.username === '' || this.password === '') {
        return
      }
      firebase.auth().signInWithEmailAndPassword(this.username, this.password)
        .then((data) => {
          this.$emit('LoginFormEvent', 0, data);
        }).catch((error) => {
          this.$emit('LoginFormEvent', -1, error.code);
        })
    }
  }
}
</script>
```

# ログインページの作成
上で作ったコンポーネントを使用するページを作成します。
意識する事は、`LoginFormEvent`でイベントが上がってくるからそこをちゃんとv-onで受けてあげて実装する事と
コンポーネントを使うための宣言`import ~`と`components: { ~ }`を行うこと。
ログイン成功時にstoreにログイン情報をディスパッチしていますが、ひとまずここでは無視してください。詳しくは別ページにて説明します。
middlewareでの処理も追加していますが、これもここでは無視でよいです。ページとコンポーネントの関係とは別の話なので。別ページで説明します。

```vuejs:~/pages/user/login
<template>
  <!-- v-onでコンポーネントから$emitで渡されるデータを受けられるようにしておく -->
  <login-form v-on:LoginFormEvent="LoginEvent"></login-form>
</template>
<script>
// コンポーネントを使用するための宣言
import LoginForm from '../../components/LoginForm.vue'
export default {
  // コンポーネントを使用するための宣言
  components: {
    LoginForm
  },
  middleware: 'must-not-be-authenticated',
  methods: {
    LoginEvent (resultCode, data) {
      if (resultCode < 0) {
        console.log("ログインに失敗しました")
        this.$router.push({path: '/user/login'})
      }
      // storeにログイン情報をセット
      console.log(resultCode, data)
      this.$store.dispatch('setLoggedIn')
      this.$store.dispatch('setEmail', data.user.email)
      this.$store.dispatch('setFireID', data.user.uid)
      
      // rootページに遷移 
      this.$router.push({path: '/'})
    }
  }
}
</script>
```
# サインアップフォームの作成
同様にサインアップページのフォームも作成します。

```vuejs:~/components/SignupForm.vue
<template>
  <v-layout
    justify-center
  >
    <v-flex
      xs12
      sm8
      md6
    >
      <v-card
        color="blue-grey darken-2"
      >
        <v-layout column wrap>
          <v-flex pt-3 pb-3 pl-5 pr-5 ma-3>
            <v-card-title class="headline">
              アカウントを作成する
            </v-card-title>

            <v-divider></v-divider>
            <v-text-field
              v-model="username"
              label="E-mail Address"
              required
            >
            </v-text-field>
            <v-text-field
              :append-icon="visible1 ? 'visibility' : 'visibility_off'"
              :rules="[rules.required, rules.min]"
              :type="visible1 ? 'text' : 'password'"
              name="input-10-2"
              label="Password"
              hint="At least 8 characters"
              v-model="password"
              @click:append="visible1 = !visible1"
            ></v-text-field>
            <v-text-field
              :append-icon="visible2 ? 'visibility' : 'visibility_off'"
              :rules="[rules.required, rules.confirmMatch]"
              :type="visible2 ? 'text' : 'password'"
              name="input-10-2"
              label="Confirm password"
              v-model="confirm_password"
              @click:append="visible2 = !visible2"
            ></v-text-field>
            <v-layout
              align-center
              justify-center
            >
              <v-btn 
                outline
                color="primary"
                @click="signupWithEmail"
              >
                Create Account
              </v-btn>
            </v-layout>

          </v-flex>
        </v-layout>
      </v-card>
    </v-flex>
  </v-layout>
</template>
<script>
import firebase from '~/plugins/firebase.js'
export default {
  data () {
    return {
      username: '',
      password: '',
      confirm_password: '',
      visible1: false,
      visible2: false,
      rules: {
        required: value => !!value || 'Required.',
        min: v => v.length >= 8 || 'Min 8 characters',
        confirmMatch: v => v == this.password || 'Unmatch password'
      }
    }
  },
  methods: {
    signupWithEmail () {
      if (this.username === '' || this.password === '' || this.confirm_password === '') {
        return
      }
      if ( this.password != this.confirm_password ) {
        return
      }

      firebase.auth().createUserWithEmailAndPassword(this.username, this.password)
        .then(data => {
          this.$emit('SignupFormEvent', 0, data);
        }).catch(function (error) {
          this.username = ''
          this.password = ''
          this.confirm_password = ''
          this.$emit('SignupFormEvent', -1, error);
        })
    }
  }
}
</script>
```

# サインアップページの作成

```vuejs:~/pages/user/login
<template>
  <signup-form v-on:SignupFormEvent="signupEvent"></signup-form>
</template>
<script>
import SignupForm from '../../components/SignupForm.vue'
export default {
  components: {
    SignupForm
  },
  methods: {
    signupEvent (resultCode, data) {
      if (resultCode < 0) {
        console.log("ユーザーの作成に失敗しました")
        this.$router.push({path: '/user/signup'})
      }
      console.log("data:",data)
      this.$router.push({path: '/user/login'})
    }
  }
}
</script>
```

# まとめ
作成したログインページは以下
![login.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283352/f2368422-6d1d-a097-4147-df5cb8108c37.png)

作成したサインアップページは以下
![signup.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283352/b6cccdce-20a6-0d47-fccd-be59c10c461a.png)



細かい部分はまだまだだけど、コンポーネントとページの作成についてはこんな感じかな。