vue-cli + vuetify + vue-routerで映画お気に入りサイトの作成[画面作成編]

普段業務でReactを触っているのですが、Vueは少ししか触ったことがなかったので、簡単なサイト作成をしながら勉強していこうと思います。

手順をずらずら書いているだけですが、誰かの役に立てれば幸いです。

今回の記事では以下の記事のような動きをする部分まで作成していきます。

![test.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/72269fc6-eead-2122-3b51-ff786f6a3d1e.gif)


#Vue CLIをインストール
Nodeがインストールされている前提で進めていきます。

```
$ npm install -g @vue/cli
```
```
$ vue --version
3.9.2
```
無事に最新バージョンがインストールされました。

#プロジェクトの作成
作りたいアプリケーション名でプロジェクトを作成します。
今回、お気に入りの映画を登録するアプリを作りたいので、favorite-movieというプロジェクト名にしました。

```
$ vue create favorite-movie(好きなプロジェクト名)
```
プリセットを選んでください、と言われた場合はdefaultで良いかと思います。

```
$ cd favorite-movie
$ ls
README.md		package-lock.json	src
babel.config.js		package.json
node_modules		public

```

無事にプロジェクトの作成がされました。

#VuetifyとVue Routerのインストール
次に必要なライブラリを入れていきます。
今回は、Vue Router と Vuetifyを使用していきます。

Vue Routerは、ルーティングが出来るライブラリ。
Vuetifyは、マテリアルデザインを簡単に構築できるコンポーネントです。

```
$ vue add vuetify
$ npm install vue-router
```
でvuetifyとVue Routerをインストールします。
インストールが終了したら、サーバを立ち上げて表示を確認します。

```
$ npm run serve
```

```
 App running at:
  - Local:   http://localhost:8080/
  - Network: http://.........:8080/
```
ローカルサーバーが立ち上がり、URLが表示されます。
ブラウザで確認すると以下の画像のようになります。

<img width="1131" alt="スクリーンショット 2019-07-07 11.41.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/4608e345-e19d-ed40-3f3d-0745e589d2af.png">

#仮の画面を作成
ここまで確認できたら、実際に画面を作成していきます。
まずはルーティングで仮の画面を作成してきます。
・ホーム画面
・一覧画面
の２つを作成していきます。


```vue:src/components/Home.vue
<template>
  <v-container>
    <h1>ホーム画面</h1>
    <router-link to="/list">Go to List</router-link>
  </v-container>
</template>
```

```vue:src/components/List.vue
<template>
  <v-container>
    <h1>一覧画面画面</h1>
    <router-link to="/">Go to Home</router-link>
  </v-container>
</template>
```

#ルーティングの作成

routes/index.jsを作成して、以下の内容を記述します。

```javascript:src/router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './components/Home';
import List from './components/List';

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/list',
      name: 'list',
      component: List
    }
  ]
});
```

main.jsに追記します。

```javascript:src/main.js
import Vue from 'vue';
import './plugins/vuetify';
import App from './App.vue';
import router from './router'; // 追記

Vue.config.productionTip = false;

new Vue({
  router, // 追記
  render: h => h(App)
}).$mount('#app');
```

最後にApp.vueを以下のようにします。

```javascript:src/App.vue
<template>
  <v-app>
    <v-toolbar app>
      <v-toolbar-title class="headline text-uppercase">
        <span>Vuetify</span>
        <span class="font-weight-light">MATERIAL DESIGN</span>
      </v-toolbar-title>
      <v-spacer></v-spacer>
      <v-btn flat href="https://github.com/vuetifyjs/vuetify/releases/latest" target="_blank">
        <span class="mr-2">Latest Release</span>
      </v-btn>
    </v-toolbar>

    <v-content>
      <router-view />
    </v-content>
  </v-app>
</template>
```

ここまでで以下のようになると思います。
リンクをクリックしてページが切り替わることが確認できました。

<img width="1071" alt="スクリーンショット 2019-07-09 2.24.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/df08824b-9fd8-06df-3b3f-a9dd76fb9a4c.png">


#ヘッダー部分の編集
App.vueに書いてあるヘッダー部分を編集します。
App.vueの見通しが悪くなるので、Headerコンポーネントに分けていきたいと思います。
src/components/Header/index.vueを作成し、ヘッダー部分を移植します。

```html:src/components/Header/index.vue
<v-toolbar app>
  <v-toolbar-title class="headline text-uppercase">
    <span>Vuetify</span>
    <span class="font-weight-light">MATERIAL DESIGN</span>
  </v-toolbar-title>
  <v-spacer></v-spacer>
  <v-btn flat href="https://github.com/vuetifyjs/vuetify/releases/latest" target="_blank">
    <span class="mr-2">Latest Release</span>
  </v-btn>
</v-toolbar>
```

App.vueを修正

```html:src/App.vue
<template>
  <v-app>
    <Header />
    <v-content>
      <router-view />
    </v-content>
  </v-app>
</template>

<script>
import Header from "./components/Header";
export default {
  components: { Header }
};
</script>
```

コンポーネントが分かれて見やすくなったと思います。

次に、新規登録、ログインモーダルを作成していきたいと思います。
モーダルは以下のリンクにAPIの記述があります。
https://vuetifyjs.com/ja/components/dialogs

新規登録画面

```html:src/components/Header/Signup.vue
<template>
  <v-dialog v-model="dialog" max-width="600px">
    <template v-slot:activator="{ on }">
      <v-btn round flat large color="primary" v-on="on">新規登録</v-btn>
    </template>
    <v-card>
      <v-card-title>
        <span class="headline">新規登録</span>
        <v-spacer></v-spacer>
        <v-btn icon @click="dialog = false">
          <v-icon>close</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text>
        <v-container grid-list-md>
          <v-layout wrap>
            <v-flex xs12>
              <v-text-field label="ユーザー名" required></v-text-field>
            </v-flex>
            <v-flex xs12>
              <v-text-field label="メールアドレス" required></v-text-field>
            </v-flex>
            <v-flex xs12>
              <v-text-field label="パスワード" type="password" required></v-text-field>
            </v-flex>
          </v-layout>
        </v-container>
      </v-card-text>
      <v-card-actions>
        <v-container grid-list-md>
          <v-btn block color="success" dark @click="dialog = false">登録</v-btn>
        </v-container>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script>
export default {
  data() {
    return {
      dialog: false
    };
  }
};
</script>
```

ログイン画面

```html:src/components/Header/Signup.vue
<template>
  <v-dialog v-model="dialog" max-width="600px">
    <template v-slot:activator="{ on }">
      <v-btn round flat large v-on="on">ログイン</v-btn>
    </template>
    <v-card>
      <v-card-title>
        <span class="headline">ログイン</span>
        <v-spacer></v-spacer>
        <v-btn icon @click="dialog = false">
          <v-icon>close</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text>
        <v-container grid-list-md>
          <v-layout wrap>
            <v-flex xs12>
              <v-text-field label="メールアドレス" required></v-text-field>
            </v-flex>
            <v-flex xs12>
              <v-text-field label="パスワード" type="password" required></v-text-field>
            </v-flex>
          </v-layout>
        </v-container>
      </v-card-text>
      <v-card-actions>
        <v-container grid-list-md>
          <v-btn block color="success" dark @click="dialog = false">ログイン</v-btn>
        </v-container>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script>
export default {
  data() {
    return {
      dialog: false
    };
  }
};
</script>
```

ヘッダーコンポーネントに埋め込みます。

```html:src/components/Header/index.vue
<template>
  <v-toolbar app>
    <v-toolbar-title class="headline text-uppercase">
      <span class="font-weight-light">Favorite Movie</span>
    </v-toolbar-title>
    <v-spacer></v-spacer>
    <Login />
    <Signup />
  </v-toolbar>
</template>

<script>
import Signup from "./Signup";
import Login from "./Login";
export default {
  components: { Signup, Login }
};
</script>
```

ここまでで以下の画像の通りになります。

<img width="1004" alt="スクリーンショット 2019-07-16 2.24.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/e8b0f4b5-eda2-39bf-68b8-28b822ae1928.png">


#バリデーションの追加
新規登録画面にバリデーションを追加していきます。
スター数も多いVeeValidateを使用していこうと思います。
https://baianat.github.io/vee-validate/

以下のコマンドをターミナル上で実行して、インストールします。

```
$ npm install vee-validate --save
```

main.jsにVeeValideteをインポートします。
ついでに日本語のエラーメッセージが良いので、言語ファイルもインポートしておきます。

```javascript:src/main.js
import Vue from 'vue';
import VeeValidate, { Validator } from 'vee-validate'; // 追加
import ja from 'vee-validate/dist/locale/ja'; // 追加
import './plugins/vuetify';
import App from './App.vue';
import router from './router';

Vue.config.productionTip = false;
Vue.use(VeeValidate); // 追加
Validator.localize('ja', ja); // 追加

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

あとは新規登録モーダルのフォーム部分を以下のように変更し、スクリプトを書き加えるとバリデーションが適応されます。

```vuejs:src/components/Header/Signup.vue
<!-- フォーム部分 -->
<v-card-text>
  <v-container grid-list-md>
    <v-layout wrap>
      <v-flex xs12>
        <v-text-field
          v-model="name"
          v-validate="'required'"
          data-vv-name="ユーザー名"
          :error-messages="errors.collect('ユーザー名')"
          label="ユーザー名"
        />
      </v-flex>
      <v-flex xs12>
        <v-text-field
          v-model="email"
          v-validate="'required|email'"
          data-vv-name="メールアドレス"
          :error-messages="errors.collect('メールアドレス')"
          label="メールアドレス"
        />
      </v-flex>
      <v-flex xs12>
        <v-text-field
          type="password"
          v-model="password"
          v-validate="'required|alpha_num|min:6'"
          data-vv-name="パスワード"
          :error-messages="errors.collect('パスワード')"
          label="パスワード"
        />
      </v-flex>
    </v-layout>
  </v-container>
</v-card-text>
```



```vuejs:src/components/Header/Signup.vue
<script>
// スクリプト部分
export default {
  data() {
    return {
      name: "",
      email: "",
      password: "",
      dialog: false
    };
  },

  methods: {
    async signup() {
      if (await this.$validator.validateAll()) {
        this.dialog = false;
        this.name = this.email = this.password = "";
        this.$validator.reset();
        this.$router.push("/list");
      }
    }
  }
};
</script>
```

バリデーションに成功していたら、一覧URLに飛ばすようにしました。
以下の画像のように、エラーメッセージも日本語になっていますね！

<img width="724" alt="スクリーンショット 2019-07-20 12.39.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/648a076a-5142-d6df-9b02-93ee5cf49e98.png">

ここまでの動作が確認できたら、ログイン画面も同じように実装します。
コードはGithubにあげますので、省略します！

あとは、ホーム画面と一覧画面をコンポーネントを組み合わせて作成します。
作成後のコードもGitにあげておきます！

https://github.com/KazukiSadasue/vue-favorite-movie/tree/step1

次回はFirebaseを使用してログイン機能を実装したりする予定です。