Laravel MixでVue.js＋Bootstrapの簡単なログインフォームを作る。

FIRST PLAN株式会社のフロントエンドエンジニアtakeです。

**sampleText**

さて、今回はテストでログインフォームを作ったので良ければご覧ください。

#こんな事をやりました
[![Image from Gyazo](https://i.gyazo.com/644c2e21a1b644566efba51edf5ec176.gif)](https://gyazo.com/644c2e21a1b644566efba51edf5ec176)

#環境設定
まずはVue.js。
今回はLaravel内で使っているので、LaravelでVue.jsを使えるようにするには以下のコマンドを実行します。

```Bash
$ composer require laravel/ui
$ php artisan ui vue --auth
```


すぐにvueを始めたい場合は[公式ドキュメント](https://jp.vuejs.org/v2/guide/installation.html)にある通りCDNでも使えますし、初めからwebpackの環境構築がされている[「Vue CLI」](https://cli.vuejs.org/)もあるので活用してみてください。
ただ今回は環境設定の記事ではないのでそこら辺は割愛します。

そして次にBootstrapですが、[BootstrapVue](https://bootstrap-vue.org/)を使用します。
機能は同じでBootstrapの記法もそのまま使えるのですが、jQuery依存だったコンポーネントがVueのディレクティブが使えるように拡張されています。

```shell
$ npm install bootstrap-vue bootstrap
```

でインストールし、
以下のコードをメインのjsファイルに追加します。

```javascript:
import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);
```

後は好みの問題なのですが、[FontAwesome](https://fontawesome.com/)と[vue-sweetalert2](https://www.npmjs.com/package/vue-sweetalert2)をインストールします。

vue-sweetalert2もBootstrapVueと同じで機能は元のsweetalert2と変わりませんが、Vueでそちらを使うとアイコン部分で表示崩れが起きる事があるのでそれを修正したものになります。
Bootstrapと同様にこちらもインストールします。

**FontAwesome**

```shell
$ npm install --save @fortawesome/fontawesome-free
```

```javascript
import '@fortawesome/fontawesome-free/css/all.css';
```

**vue-sweetalert2**

```shell
$ import VueSweetalert2 from 'vue-sweetalert2';
```
 
```javascript
import 'sweetalert2/dist/sweetalert2.min.css';
Vue.use(VueSweetalert2);
```

#コード
##HTML
今回は以下のような感じで、`card`と`input-group`を使って実装しました。

```html:HTML
<template>
  <section class="login-form d-flex flex-solumn justify-content-center align-items-center w-100 text-center">
    <div class="login-form__field card rounded-0 pt-5 pb-3">
      <i class="login-form__budge fas fa-user-circle text-primary bg-white rounded-circle"></i>
      <div class="card-body">
        <h1 class="login-form__heading card-title text-primary font-weight-bold mb-0">Sign in</h1>

        <hr class="bg-primary mt-0 mb-4">

        <p class="card-text text-muted mb-4">ユーザー名とパスワードを入力してください。</p>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-user"></i>
            </span>
          </div>
          <input class="form-control" type="text" placeholder="Username" v-model="login.user_name">
        </div>

        <div class="input-group mx-auto mb-4">
          <div class="input-group-prepend">
            <span class="input-group-text bg-primary text-white" id="basic-addon1">
              <i class="fas fa-key"></i>
            </span>
          </div>
          <input class="form-control rouded-0" type="password" placeholder="Password" v-model="login.password">
        </div>

        <button class="login-form__button btn btn-primary w-75 mb-4" type="button" :disabled="checkInput" @click="signIn">Login </button>

        <div class="login-form__attention d-flex justify-content-between my-o mx-auto">
          <div class="custom-control custom-checkbox">
            <input type="checkbox" class="custom-control-input" id="remember-me" v-model="login.remember">
            <label class="custom-control-label" for="remember-me">情報を記憶する</label>
          </div>
          <a class="card-link" href="#">
            <u>パスワードを忘れた方はこちら</u>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>
```

##CSS
飽くまでBootstrapベースなので、scssでBEMを採用して影のエフェクト以外は軽く整える程度にしました。

```scss:scss
.login-form {
  &__field {
    position: relative;
    max-width: 30rem;
    //影エフェクト
    &:after {
      content: '';
      position: absolute;
      bottom: 20px;
      right: 55px;
      z-index: -1;
      width: 50%;
      height: 100px;
      box-shadow: 100px 0 10px 15px rgba(0, 0, 0, .3);
      transform: skew(-40deg);
    }
  } //__field
  &__budge {
    position: absolute;
    top: -2rem;
    left: 50%;
    font-size: 6rem;
    transform: translateX(-50%);
  } //__budge
  &__attention {
    font-size: 0.6rem;
  } //__attention
  &__button {
    border-radius: 0!important;
  } //__button
} // .login-form
```

##JavaScript
以下がデフォルトのVueの記述方法になります。

ざっくり説明すると`data`は変数等を格納する場所・`computed`は変更があるとリアルタイムで値を再計算してくれる場所・`methods`は文字通りメソッドを格納する場所です。
他にも`watch`やライフサイクルフック等開発に便利な機能が色々とあるので、詳しくは[公式ドキュメント](https://jp.vuejs.org/index.html)をご参照ください。

さて、まず`data`内の`login`は「ユーザーネーム・パスワード・ユーザー情報を記憶するか否か」を保持しています。
html内に`v-model="login.user_name"`といった記述があると思いますが、この`v-model`によって双方向データバインディングを実現しています。
どちらかが変更された段階でもう片方にも自動で変更が同期されるため、自分で逐一`document.querySelector(element)～`をする必要がなくなります。

次は`computed`ですが、今回は`login.user_name`と`login.password`が条件に組み込まれていますが、どちらかの値が変更された段階で自動で値を更新します。
メソッド内に`if (!this.checkInput)`と指定されている関数がありますが、このように使用する事で他の場所で毎回入力を確認する必要がなくなります。
ちなみに`button`にある`:disabled="checkInput"`にも使用されていますが、まずこの`:`マークは`v-bind`といってhtml内の属性（`class`や`href`等)に付ける事で内部でJavaScriptのコードを直接使用可能にするものです。
今回はこれらを利用して`disabled`の値をフォーム入力の有無によって動的に変更しています。

最後に`methods`は一番簡単で、関数を格納する場所になります。
とりあえず確認用に`v-model`で同期した値をsweetalert2で表示しています。
イベントハンドリングはhtml側で`@event="hoge"`のように行います。（今回の場合は`@click="signIn"`）
ちなみにこの`@click`以下はコンパイル時に削除されるため、htmlを汚染する心配はありません。

```javascript:JavaScript
export default {
  data() {
    return {
      //ログイン情報
      login: {
        user_name: '',
        password: '',
        remember: false
      }
    }
  },
  computed: {
    //ユーザーネームとパスワードの両者が入力されている事を判定する値、自動で変更を検知し反映してくれる
    checkInput() {
      return (!this.login.user_name || !this.login.password) ? true : false;
    }
  },
  methods: {
    signIn() {
      //ユーザーネームとパスワードの両者が入力されていた時のみ発火する。
      if (!this.checkInput) {
        //v-modelで同期した値を出力する
        this.$swal({
          icon: 'info',
          title: 'Information',
          text: `
            ID: ${this.login.user_name},
            Password: ${this.login.password},
            Remember: ${this.login.remember}
          `
        })
      }
      return;
    } //signIn
  }
}
```

最後に

```shell
$ npm run dev
```

でコンパイルして確認すると...

[![Image from Gyazo](https://i.gyazo.com/644c2e21a1b644566efba51edf5ec176.gif)](https://gyazo.com/644c2e21a1b644566efba51edf5ec176)

キチンと動いています。

よろしければ参考にしてみて下さい。