トップ/記事一覧

Nuxt.js + Vuex におけるフォームバリデーションのベストプラクティスを考える

📆2020/05/04(最終更新日:2020/05/03)🔖 NuxtJSVue.js

この記事は最終更新日から1年以上が経過しています。内容が古い箇所がある可能性があるためご注意ください。

Nuxt.js + Vuex でフォームバリデーションを実装する機会が増えたので、ベストプラクティスについて考えてみました。いろいろな実現方法があるとは思いますが、自分なりの答えが出たので、記事にしておきたいと思います。VeeValidator 等のライブラリ等は使わない前提です。

Action 層でバリデーションをかける

バリデーションの判定は Action 層で行います。Page 層にはロジックは書かない方針で一貫した設計をしているため、Page 層にはバリデーションロジックは書きません。

Action は、以下のようコードになります。

JavaScript

async createTodo({ dispatch }, form: TodoForm): Promise<void> { assertTodo(form); // 第二引数で渡ってくる form の妥当性をチェック // DO SOMETHING // form -> model // API 通信 // commit 等 }

Vuex では dispatch したときの第二引数としてオブジェクトを渡すことができるので、このオブジェクトに対し、バリデーション判定を行います。具体的なロジックについては割愛します。

Action 層の責務としては、以下の2つになります。

  • 引数として渡ってきた Form オブジェクトの妥当性をチェックする
  • Form オブジェクトを API 通信に適した型に変換し、処理を行う
  • 図にすると、以下のような形になります。

    バリデーションロジックでは、独自のバリデーションエラーを作って throw する

    バリデーションにひっかかった場合(👆の例でいう assertTodo で判定して、ひっかかった場合)には、独自のエラーを発生させ、それを投げることとしています。Error を拡張した独自のエラー ValidationError を自前で定義し、フィールドとして、エラーの内容を詰めるようにしています。

    以下は、僕が定義している独自エラーの例です。

    JavaScript

    export class ValidationError extends Error { errors: Array<ValidationErrorObj>; constructor(errors: Array<ValidationErrorObj>, message: string = 'ValidationError') { super(message); this.errors = errors this.name = 'ValidationError'; } }

    バリデーションエラーに引っかかった場合には、以下のようにエラーを throw するようにしています。errors には、エラーに引っかかった要素名と、エラー文言を詰めたオブジェクトの配列を渡すようにしています。

    JavaScript

    throw new ValidationError(errors);

    Page 層でのバリデーションエラーハンドリング

    Page から Action の呼び出しは dispatch で行います。dispatch の返り値は Promise なので、例外が投げられた場合の処理を catch ブロックにまとめることが出来ます。

    エラーのクラスを instanceof で判定してやって、バリデーションエラーだった場合には、エラーメッセージを出す等の処理を行います。また、想定していない例外(たとえば、API 通信で発生したネットワークエラー等)の場合は、そのまま例外を throw して、Sentry 等に処理を委ねることにしています。

    以下はその例です。await/catch で書くのは好み。

    JavaScript

    const res = await this.$store.dispatch('todo/createTodo', this.form) .catch((e) => { if (e instanceof ValidationError) { this.errors = e.errors; } else { throw e; } });

    errors フィールドに、エラーの内容を詰めているので、Page 側で、エラーの内容を表示させたりすることができます。ここはアラート出すなり、ビューに表示するなり、お好みでどうぞ。

    ちなみに、 Jest を使ったバリデーションのテストは以下のようになります。Action 単位でテストするのが好みなので、こんな感じで。

    JavaScript

    await store.dispatch('createTodo', form).catch((e: any) => { expect(e instanceof ValidationError).toBeTruthy(); })