Web制作・プログラミング関連のおすすめレビュー記事はこちら

LaravelのエラーをVuexを使ってVueコンポーネント上で表示する

Laravel

今回はLaravel側でバリデーションしたエラーをVueコンポーネント側で表示する方法を解説します。

というわけで今回はLaravel×VueのSPAでエラー表示する方法を解説します。

今回解説すること

今回はあくまでLaravelでのエラーをVueコンポーネントに表示させる方法を解説します。

基本的なLaravel・Vue・Vuexについての解説は割愛するので他の記事や書籍を参考にしてください。

Vuexに関する解説はこちら!

この記事のゴール・目標物の確認

まずはイメージとして今回作成するものの確認です。

フォルダを作成する際に、条件を満たさないリクエストを送るとエラーが表示されます。

Laravel側でバリデーションをかける。

まずはLaravel側でFormRequestを使ってバリデーションをかけていきます。

バリデーション 5.5 Laravel

まずコマンドラインで、リクエストを作成しましょう。

php artisan make:request HogeRequest

今回はフォルダを新規追加することを想定して、FolderRequestを作成しました。

今回はフォルダ名を"必須"かつ"10文字以内"の制限をかけているだけです。

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use App\Folder;
class FolderRequest extends FormRequest
{
    public function authorize()
    {
-       return false; //ここを変更
+       return true;
    }

    public function rules() //ここにバリデーションを追加
    { 
        return [
            'body' => [
                'required',
                'max:10',       
            ]
        ];
    }

    public function attributes()
    {
        return [
            'body' => 'フォルダ名',
        ];
    }
}

上で作成したリクエストをコントローラーのstoreメソッドで使用することで、メソッドを行う前にリクエストでのバリデーションがかかります。

    public function store(FolderRequest $request, Folder $folder) 
    {      
            $folder->fill($request->all());
            $folder->save();
            $folders = Folder::where('user_id', $request->user()->id)->get()->sortByDesc('created_at');
            return $folders;
    }

ここまでで、FoldersControllerのstoreメソッドが実行される前にFolderRequestでのバリデーションがかかるようになりました。

route/api.php
Route::post('/folders/add', 'FoldersController@store');

ルーティングを定義するのを忘れてましたね。

"api/folders/add"にpostメソッドでリクエストが来たときに先程のstoreメソッドが呼び出されるようにしておきます。

Vuexを使ってfolderモジュールを定義

folderモジュールを定義」と書きましたが、要はfolder関連のデータをVuexで管理できるように登録するということです。

import Vue from "vue";
import Vuex from "vuex";

import folder from "./folder"

Vue.use(Vuex);

const store = new Vuex.Store({
    modules: {
        folder,
    }
});

export default store;


import axios from "axios";
const state = {
  folders: null,
  folderErrorMessages: null
}

const getters = {
  allfolders: state => state.folders
}

const mutations = {
  setFolders(state, folders) {
    state.folders = folders
  },
  setFolderErrorMessages(state, messages) { 
    state.folderErrorMessages = messages
  }
}

const actions = {
  async add(context, data) {
    const response = await axios.post("/api/folders/add", data)//Laravelで定義したフォルダ追加APIを叩く
    //成功したときの処理
    if (response.status === 200) {
      context.commit('setFolders', response.data)//得られた結果をsetFoldersに渡して、stateを更新
      return false //処理を終了
    }
    //失敗したときの処理
    if (response.status === 422) { 
      context.commit('setFolderErrorMessages', response.data.errors)
    } 
  },
}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
};

folder.jsのstateには現在のフォルダ一覧を管理するfoldersとフォルダ関連のエラーを管理するfolderErrorMessagesを定義してあります。

まずはフォルダを追加するときに呼び出されるactionsのaddアクションを見てみましょう。

  async add(context, data) {
    const response = await axios.post("/api/folders/add", data)//Laravelで定義したフォルダ追加APIを叩く
    //成功したときの処理
    if (response.status === 200) {
      context.commit('setFolders', response.data)//得られた結果をsetFoldersに渡して、stateを更新
      return false //処理を終了
    }
    //失敗したときの処理
    if (response.status === 422) { 
      context.commit('setFolderErrorMessages', response.data.errors)
    } 
  },

まずはaxiosでLaravel側に定義してあるフォルダ追加のAPIにアクセスし、結果をresponseに格納します。

response.statusとすることで返ってきたレスポンスのステータスコードで条件分岐しています。

もし200(成功)だったらsetFoldersミューテーションを呼び出してstateのfoldersを更新。

もし、422(失敗)だったらsetFolderErrorMessagesミューテーションを呼び出してstateのfolderErrorMessagesを更新しています。

(※本来は他のステータスコードを考慮したり、定数で置き換えるべきですが簡易化のために割愛します。)

ここまでで、もしフォルダの追加に失敗したらVuexのstoreのfolderErrorMessagesにエラーメッセージが格納されるようになりました。

Vueコンポーネントにエラーメッセージを表示する

ここまでくれば後はエラーコンポーネントを作成して、エラーメッセージを表示させるだけです。

まずはValidationError.vueというコンポーネントを作成しましょう。

<template>
  <div v-if="validationErrors" class="m-2">
    <ul class="alert alert-danger list-unstyled">
      <li v-for="(value, index) in validationErrors" :key="index">{{value}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    errors: Object
  }, //errorsプロパティで親コンポーネントからのエラーを受け取る。
  computed: {
    //propsを直接使わないようにcomputedで詰め替えを行う。
    validationErrors() {
      let errors = Object.values(this.errors)
      errors = errors.flat()
      return errors;
    }
  }
}
</script>

このエラーコンポーネント内ではVuexからの値の参照は行いません。

あくまでも親コンポーネントからもらったエラー情報propsで受け取り、表示するという役割にとどめます。

それではエラーを表示したい親コンポーネントにこのValidationError.vueを組み込んで行きましょう。

今回は以下のようにモーダル内にエラーが表示されるようにします。

親コンポーネントに当たるFolderModal.vueを作成。

<template>
  <div id="overlay">
    <div class="modal-dialog modal-sm" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h4 class="modal-title w-100" id="myModalLabel">フォルダを追加</h4>
          <button type="button" class="close" @click="$emit('close')">
            <span aria-hidden="true">×</span>
          </button>
        </div>
        <div class="modal-body">
      //エラーメッセージコンポーネント
          <validation-errors :errors="folderErrors" v-if="folderErrors"/>
          <form @submit.prevent="$emit('add', new_folder)">
            <input v-model="new_folder.body" type="text" class="form-control m-2">
            <div class="input-group">
              <button class="btn btn-md btn-default light-red m-2 px-3" type="submit" :disabled="!new_folder.body">
                作成
              </button>
              <button class="btn btn-md btn-light m-2 px-3" type="button" @click="$emit('close'),clearError">
                キャンセル
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import ValidationErrors from "../errors/ValidationErrors"
export default {
  components: {
    ValidationErrors
  },
  data() {
    return {
      new_folder: {
        body: ''
      }
    }
  },
  methods: {
    clearError() {
      this.$store.commit('folder/setFolderErrorMessages', null)
    },
  computed: {
    ...mapState({
      folderErrors: state => state.folder.folderErrorMessages
    })
  },
  created() {
    this.clearError() //モーダル表示前にエラーをクリア
  }
}
</script>

<style>
 //省略
</style>

まずは、mapStateで先程定義したVuexのfolderErrorMessagesfolderErrorsという変数で参照します。

  computed: {
    ...mapState({
      folderErrors: state => state.folder.folderErrorMessages
    })
  },

あとはfolderErrorsの有無でエラーコンポーネントを出し分けつつ、エラー情報を子コンポーネントであるValidationErrors.vueにv-bindで渡しています。

<validation-errors v-if="folderErrors" :errors="folderErrors"/>

ここまでで正常にVue側でエラーを表示できたと思います。

ただ、まだ一つ問題があります。

それは一度エラーが出てしまうと、ページをリロードするまでエラーが消えないということです。

これを解消するために、clearErrorsメソッドを定義して、createdのタイミングで自動的に実行するように設定してあります。

 clearError() {
      this.$store.commit('folder/setFolderErrorMessages', null)
},
  created() {
    this.clearError() //モーダル表示前にエラーをクリア
  }

これでモーダルを開くたびにエラーは消えるようになったので、完成です!

お疲れさまでした!!

最後に

今回はLaravel×Vueのエラー表示を解説しました。

当ブログではVue関連の解説記事を多く取り扱っているのでぜひ読んで見てください。

Vue関連の人気記事はこちら!

Vue入門書のおすすめはこちら!

タイトルとURLをコピーしました