【Vue.js】yupを使用して配列項目を条件によってチェック内容を変えたい

## 概要
yupは[compositionAPIでyupとvee-validateを使ったバリデーションをする](https://zenn.dev/cryptobox/articles/0c442917e2b47b)の記事にある通り、バリデーションルールを宣言的に記述することのできるライブラリです。項目毎のチェック内容について、ソースコードの見通しがけっこう良くなるので、使用感は悪くないライブラリと思います。
今回yupを使用し配列項目に対して、条件によってチェック内容を変えるVueの実装を紹介します。

## 実装方針
Reactの記事ではありますが、[react-hook-form(yup)を使った重複チェックバリデーション](https://gist.github.com/pipopotamasu/e279a572b6c843af305b0615289f93f9)にある通り、個別にバリデーションロジックを作成します。今回はこのバリデーションロジックの中で条件判定を行い、配列の項目毎にチェック内容を変えます。


## 実装サンプルの前提
* Vue3を前提とします。
* vee-validate4を併せて使用します。
* サンプルでは、以下のようにファイルの登録を可変で管理するものを、実装します。

```vue
<template>
  <v-btn
    class="ml-2 mb-1"
    icon
    width="20"
    height="20"
    color="indigo"
    @click="pushFileExec"
  >
    <v-icon>mdi-plus</v-icon>
  </v-btn>
  <fieldset v-for="(fileField, idx) in fileFields" :key="idx">
    <br />
    <ErrorMessage :name="`referenceFiles[${idx}].fileName`" class="error" />
    <Field
      :id="`fileName_${idx}.fileName.id`"
      :name="`referenceFiles[${idx}].fileName`"
      style="width: 270px"
      v-slot="{ field }"
    >
      <v-text-field
        v-bind="field"
        label="ファイル名"
        style="width: 270px"
        v-model="fileField.value.fileName"
      ></v-text-field>
    </Field>
    <ErrorMessage :name="`referenceFiles[${idx}].file`" class="error" />
    <Field
      :id="`file_${idx}.file.id`"
      :name="`referenceFiles[${idx}].file`"
      v-slot="{ handleChange }"
    >
      <input type="file" style="width: 270px" @change="handleChange" />
    </Field>
    <!-- ここは値保持のためなので非表示で設定 -->
    <span v-show="false">
      <Field
        :id="`referenceFiles_${idx}`"
        :name="`referenceFiles[${idx}].registeredId`"
      ></Field>
    </span>
    <br />
    <br />
    <v-btn
      class="ml-2 mb-1"
      icon
      width="20"
      height="20"
      color="indigo"
      @click="
        () => {
          removeFileExec(idx);
        }
      "
    >
      <v-icon>mdi-minus</v-icon>
    </v-btn>
  </fieldset>
</template>

<script>
import { defineComponent } from "vue";
import { useFieldArray, useForm, ErrorMessage, Field } from "vee-validate";
import * as yup from "yup";

export default defineComponent({
  components: {
    ErrorMessage,
    Field,
  },
  setup() {
    const {
      remove: removeFile,
      push: pushFile,
      fields: fileFields,
      update: updateFile,
      replace: replaceFile,
    } = useFieldArray("referenceFiles");

    const pushFileExec = () => {
      pushFile({
        fileName: "",
        file: undefined,
        registeredId: undefined,
      });
    };

    const removeFileExec = (idx) => {
      removeFile(idx);
    };

    return {
      fileFields,
      pushFileExec,
      removeFileExec,
    };
  },
});
</script>

```

## チェック部分の実装サンプル
条件として上記の前提に記載している、配列内のオブジェクトにある`registeredId`の項目が未設定の場合、`file`の登録を必須にするというのを実装してみます。
以下がバリデーション部分の実装です。


```vue
<script>

// バリデーションロジックの定義
function validateFileUpload(file) {
  // チェック対象が「file」項目だが、条件判定のため親のオブジェクトをこれで取得する。
  const referenceFile = this.from[0].value;
  // IDが既に登録されている場合は無条件でOK
  if (referenceFile.registeredId) {
    return true;
  } else {
    // IDが登録されていない場合はfileの登録を必須
    if (file && file.length > 0) {
      return true;
    }
    return false;
  }
}

// 入力フォームの設定
const postRegisterSchema = yup.object({
  referenceFiles: yup
    .array()
    .of(
      yup.object().shape({
        file: yup
          .array()
          .test(
            "file-upload-check",
            "ファイルを登録してください",
            validateFileUpload // バリデーションロジック
          ),
        })
     ).strict(),
});

const formContext = useForm({
  validationSchema: postRegisterSchema,
});
</script>
```