element-uiのvalidatorの使い方

#概要
laravel+vueで作ったWebサイトでバリデーション処理を作ったのでメモ。

###formのattributeを使ったバリデーション
el-formとel-form-itemの属性を使ってバリデーションを組む。
コード量が少ないがテーブルのように複数アイテムがある場合や、APIを叩いてチェックしたい場合など静的ルールに表現できない場合は使えない。

```vue
<el-form ref="formData" :model="formData" label-position="top">
  <el-form-item :rules="rules" :prop="propname">
    ・・・
  </el-form-item>
</el-form>

(略)
// バリデーション実行
const fm: any = _this.$refs["formData"];
fm.validate((valid, fields) => {
  // バリデーションNGの場合は処理中断
  if (valid === false) {
    // 処理完了通知
    _this.$message({
      message: "入力内容が正しくない項目があります、赤色のエラー部分をご確認ください。",
      type: "warning",
      duration: 0,
      showClose: true
    });
    return;
  }
  
  //バリデーションOKの処理
  ・・
}
```
formのvalidateメソッドをコールするとコールバックで結果が返ってくる。
validがbooleanのバリデーションOK/NG、fieldsにNG時の詳細(フィールド名とルールで指定したエラーメッセージのセット)が入ってくる。

propnameにチェック対象のデータ変数を指定する。
rulesがバリデーションルール。以下にいくつかの例。

必須チェック

```vue
private rules: Object[] = [];

let requiredRule = {
  required: true,
  message: "入力必須です。",
  trigger: "blur"
};
this.rules.push(requiredRule);
```
郵便番号

```vue
let zipRules = {
  pattern: /^[0-9]{3}-[0-9]{4}$/,
  message: "正しい形式で入力してください。　例）123-4567",
  trigger: "blur"
};
this.rules.push(zipRules);
```
電話番号・FAX番号

```vue
let telFaxRules = {
  pattern: /^[-0-9]*$/,
  message: "正しい形式で入力してください。　例）1234-56-7890",
  trigger: "blur"
};
this.rules.push(telFaxRules);
```
e-Mail

```vue
let emailRules = {
  type: "email",
  message: "メールアドレス形式で入力してください。",
  trigger: "blur"
};
this.rules.push(emailRules);
```
全角チェック

```
// 英字
if (_this.charPattern.indexOf(CharPatternEnum.Alphabet) >= 0) {
  charPatternNames.push("英字");
  regExpPattern += "ａ-ｚＡ-Ｚ";
}

// 数字
if (_this.charPattern.indexOf(CharPatternEnum.Numeric) >= 0) {
  charPatternNames.push("数字");
  regExpPattern += "０-９";
}

// カタカナ
if (_this.charPattern.indexOf(CharPatternEnum.Katakana) >= 0) {
  charPatternNames.push("カタカナ");
  regExpPattern += "ァ-ヶー";
}

// スペース
if (_this.charPattern.indexOf(CharPatternEnum.Space) >= 0) {
  charPatternNames.push("スペース");
  regExpPattern += "　";
}

// ハイフン
if (_this.charPattern.indexOf(CharPatternEnum.Hyphen) >= 0) {
  charPatternNames.push("ハイフン（ ‐ ）");
  regExpPattern += "‐";
}

// 全角以外の定義が無い場合の処理
if (charPatternNames.length === 0) {
  regExpPattern = "^\x00-\xFFｧ-ﾝｦｰﾟﾞ";
}

// 正規表現パターン終端処理
regExpPattern = "^[" + regExpPattern + "]*$";

// メッセージ作成
if (charPatternNames.length >= 2) {
  charPatternMsg += "の";
}
charPatternMsg += charPatternNames.join("・") + "で入力してください。";

// ルールに追加
let charPatternRules = {
  pattern: new RegExp(regExpPattern),
  message: charPatternMsg,
  trigger: "blur"
};
this.rules.push(charPatternRules);
```
半角チェック

```vue
// 半角の場合
charPatternMsg += "半角";

// 英字
if (_this.charPattern.indexOf(CharPatternEnum.Alphabet) >= 0) {
  charPatternNames.push("英字");
  regExpPattern += "a-zA-Z";
}

// 数字
if (_this.charPattern.indexOf(CharPatternEnum.Numeric) >= 0) {
  charPatternNames.push("数字");
  regExpPattern += "0-9";
}

// カタカナ
if (_this.charPattern.indexOf(CharPatternEnum.Katakana) >= 0) {
  charPatternNames.push("カタカナ");
  regExpPattern += "ｧ-ﾝｦｰﾟﾞ";
}

// 記号
if (_this.charPattern.indexOf(CharPatternEnum.Symbol) >= 0) {
  charPatternNames.push("記号");
  // todo: とりあえず全ての記号を網羅したが、使わなそうな記号は省いた方が良いかも。。@2020/01/28 s.aono
  regExpPattern += "\x20-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E";
}

// スペース
if (_this.charPattern.indexOf(CharPatternEnum.Space) >= 0) {
  charPatternNames.push("スペース");
  regExpPattern += " ";
}

// ハイフン
if (_this.charPattern.indexOf(CharPatternEnum.Hyphen) >= 0) {
  charPatternNames.push("ハイフン（ - ）");
  regExpPattern += "\\-";
}

// 半角以外の定義が無い場合の処理
if (charPatternNames.length === 0) {
  regExpPattern = "\x20-\x7E\uFF65-\uFF9F";
}

// 正規表現パターン終端処理
regExpPattern = "^[" + regExpPattern + "]*$";

// メッセージ作成
if (charPatternNames.length >= 2) {
  charPatternMsg += "の";
}
charPatternMsg += charPatternNames.join("・") + "で入力してください。";

// ルールに追加
let charPatternRules = {
  pattern: new RegExp(regExpPattern),
  message: charPatternMsg,
  trigger: "blur"
};
this.rules.push(charPatternRules);
```

###async-validatorを使ったカスタムバリデーション
テーブルのように複数アイテムがある場合や、APIを叩いてチェックしたい場合など動的にチェックしたい場合に使う方法。
実はel-formもasync-validatorを使っている。

```vue
this.validate(function(valid: boolean) {
  // バリデーションNGの場合は処理中断
  if (!valid) {
    // 処理完了通知
    _this.$message({
      message: "入力内容が正しくない項目があります、赤色のエラー部分をご確認ください。",
      type: "warning",
      duration: 0,
      showClose: true
    });
    return;
  }

  ここにバリデーションOK時の処理
}

//バリデーション実体
validate(callback) {
  const _this = this;
  _this.serviceTypeMstData.forEach(row => {
    _this.checkInput(row, [
      "service_type_code",
      "service_item_code",
      "service_type_display_name"
    ]);
  });
  var ret = true;
  _this.serviceTypeMstData.forEach(row => {
    if (row.isValidateError) {
      ret = false;
    }
  });
  callback(ret);
}

//必須項目の入力チェック
checkInput(row: ServiceTypeMasterExData, targets: string[]) {
  const _this = this;

  var rules = {};
  targets.forEach((value: any, index: any) => {
    rules = Object.assign(rules, this.getRule(value));
  });

  var validator = new schema(rules);
  validator.validate(this.getValidateObject(row), (errors, fields) => {
    if (errors && !row.isAddButton) {
      // バリデーションエラー時
      errors.forEach((value: any, index: any) => {
        row.validate_error[value["field"]] = value["message"];
      });
    } else {
      targets.forEach((value: any, index: any) => {
        row.validate_error[value] = "";
      });
    }
    row.isValidateError = (function(row) {
      for (var key in row.validate_error) {
        if (row.validate_error[key] != "") {
          return true;
        }
      }
      return false;
    })(row);
  });
}

//対象に絞ったバリデーションルール取得
getRule(name: string) {
  return {
    service_type_code: { service_type_code: [{ required: true, message: "入力必須です。" }] },
    service_item_code: { service_item_code: [{ required: true, message: "入力必須です。" }] },
    service_type_display_name: {
      service_type_display_name: [{ required: true, message: "入力必須です。" }]
    }
  }[name];
}

//バリデーション対象オブジェクト作成
getValidateObject(row: ServiceTypeMasterExData): object {
  return {
    service_type_code: row.service_type_code,
    service_item_code: row.service_item_code,
    service_type_display_name: row.service_type_display_name
  };
}
```
静的ルールならel-formの時に使ったルールで記述できる。
動的ルールならcheckInputで判定処理を入れて結果をerrorsにbooleanで返し、fieldsに詳細をフィールド名(キー名:field)とメッセージ(キー名:message)で返せばいい。