morishitaです。 今回はJavascriptのバリデーションライブラリvalidatorjsを紹介します。
TL;DR
- いこレポではクライアントサイドバリデーションにvalidatorjsを使っています。
- validatorjsはとても柔軟に設定でき使いやすいバリデータです。
- Vue+Vuexなアプリケーションととても相性がいいです。
いこレポの記事編集機能
いこレポは見ての通り、公開しているWebサイトは特に特別な機能を持たないメディアサイトで、実装もシンプルです。 したがって表側よりも、記事を管理する機能のほうが実装的に複雑です1。
その管理機能の中でも最もコード量が多く複雑なのは記事編集機能です。
記事編集機能はVue.jsを利用してます。Vue.jsに加えてVuexも利用しています。 本文以外にも記事を分類するための属性が結構たくさんあります。 それらを公開時にはもれなく設定する必要があるので、ミスなく運用するためにバリデーションが必要です。 サーバサイドのバリデーションももちろんやっていますが、インタラクティブに不足を指摘したほうが使いやすいので クライアントサイドのバリデーションも行っています。
JavaScriptのバリデーションライブラリはたくさんありますが、次の理由からValidatorjsを採用しました。
- 他のライブラリに依存していない
- カスタムバリデータの追加が簡単
- i18n対応もしている(いコレポではあまり使っていないですが...)
Validatorjs とは
Validatorjs はJavaScriptのバリデーションライブラリです2。
バリデーションライブラリというと値を1つ渡して、それがバリデーションルールを満たすかを判定するというものが多いです。しかしVaridatorjsは A data validation library を謳っています。
どういうことかというと、JavaScriptのオブジェクトを渡すと、指定したプロパティの値がバリデーションルールを満たしているのか一度に判定してくれるのです。渡したオブジェクトのデータ全体が適正な値であるかを判定してくれます。ネストした深い階層の属性のバリデーションも可能です。
用意されているバリデーションルールも多数あり、必須チェックや文字数のチェックはもちろん、
emailやURLの形式チェックのルールもあります。また関連する属性がある値の場合のときに必須になるrequired_if
のような便利なルールもあります。
利用例
次のようなperson
オブジェクトのバリデーションを考えます。
name
属性が必須accept_mail_magazine
属性がtrue
のとき、mail
属性が必須mail
属性は E-mailアドレスの形式でなければならない
上記を Validatorjs で実装すると次のようになります。
const Validator = require('validatorjs'); // バリデーション対象のオブジェクト const person = { name: 'hogehoge', accept_mail_magazine: true, email: 'hoge@example.com', } // バリデーションルールの定義 const rules = { name: 'required', email: [{required_if: ['accept_mail_magazine',true]}, 'email'], } const v = new Validator(person, rules); // バリデータインスタンスの生成 // バリデーション check: v.check() // => true v.errors // バリデーションエラーの情報がすべて取れる
更に足りなければ独自のルールを追加することもできます。
例えば、次のバリデーションを先程のperson
オブジェクトに追加します。
languages
属性は次の選択肢から少なくとも1つは選択する必要がある。複数選択しても良い。ruby
javascript
python
kotlin
swift
others
このようなバリデーションルールは標準では用意されていません。
でも、次のようにカスタムバリデータを実装して追加することでバリデーション可能になります。
const Validator = require('validatorjs'); // カスタムバリデータの定義 function someOneTrue(value, requirement, attribute) { return Object.values(value).some((val) => val); } // 定義したカスタムバリデータを登録 Validator.register( 'some_one_true', someOneTrue, ':attribute は少なくとも1つはチェックする必要があります' ); const person = { name: 'hogehoge', accept_mail_magazine: true, email: 'hoge@example.com', languages: { // 新たに追加した属性。 ruby: false, javascript: false, python: false, kotlin: false, swift: false, others: false, } } const rules = { name: 'required', email: [{required_if: ['accept_mail_magazine',true]}, 'email'], languages: 'some_one_true', // 追加したカスタムバリデータの使用 } const v = new Validator(person, rules); check: v.check() // => true const errors = v.errors // バリデーションエラーの情報がすべて取れる
上記を実行すると errors
の中身は次のようになります。
{"languages":["languages は少なくとも1つはチェックする必要があります"]}
簡単ですね。
そして、Vue+Vuexと組み合わせると、それはもうとてもとても便利なのです。
Vue+Vuex
Vue+VuexでVaridatorjsを利用するとどう便利なのか? ということを説明するためにVuexについて少し説明します。
Vuex は Vue.js アプリケーションのための状態管理パターン + ライブラリです。Vue+Vuexなアプリケーションの場合、データを Vuexのstore
で管理するJSのオブジェクトの形で持ちます。
Flux、 Redux に影響を受けており、単方向データフローでデータの状態を管理、更新します。
VueアプリケーションではVuexのmutation
を使ってstate
を変更します。 state
の変更はそれを参照するVueコンポーネントにすぐに反映されます。
更にVuexにはゲッター(getters
)というVueコンポーネントの算出プロパティ(computed
)に対応する機構があります。これによりstate
の値をそのまま参照するのではなく計算した結果を参照することも可能です。算出プロパティ同様に、state
が更新されればゲッターにも反映され、ゲッターを参照するコンポーネントにも伝播されます。
Vue+Vuex での Validatorjs
Vue+Vuex アプリケーションでValidatorjsを使ったバリデーションをどこに実装するのが良いのでしょうか?
それは VuexのStoreのゲッターです。
次のコードはVuexストアの実装例です。
import Vue from 'vue'; import Vuex from 'vuex'; // 次のモジュールで前述のカスタムバリデータと // それを使ったルールを定義しているとします。 import {Validator, rules} from './validator'; Vue.use(Vuex); export default new Vuex.Store({ state: { person: { name: '', accept_mail_magazine: true, email: 'hoge@example.com', languages: { ruby: false, javascript: false, python: false, kotlin: false, swift: false, others: false, } }, }, getters: { validationErrors: (state) => { const v = new Validator(state.person, rules); v.check(); return v.errors; }, } });
このStoreをVueのモジュールから参照する例が次のコードです。
export default { store, computed: { validationErrors() { return this.$store.getters.validationErrors; }, }, // 〜 略 〜 }
こうしておくと、算出プロパティvalidationErrors
には store
に格納されたperson
オブジェクトの
バリデーション結果が格納されます。
person
オブジェクトが更新されるたびにバリデーション結果も更新されるので、
算出プロパティvalidationErrors
を参照するUIにもその結果が即時に反映されます。
便利かつ簡単でしょう? 一度お試しあれ。
最後に
アクトインディでは使いやすいUXを一緒に作っていく エンジニアを募集しています。
-
複雑と言っても表側がCRUDのほぼREADしかないのに対して、管理機能ではCRUDを一通り実装しているだけですが。↩
-
chriso/validator.js: String validationというよく似た名前のライブラリもあるのでお間違えのないように。↩