jquense / yup

Dead simple Object schema validation
MIT License
22.86k stars 932 forks source link

can't set the rules to validate file size and extension #2204

Open mahfuz01-2023 opened 6 months ago

mahfuz01-2023 commented 6 months ago

I am using yup to validate a form in vue3 using vee-validate

Bellow is my code. All validations are working as expected except file. Not sure how to approach it, any help is much appreciated.

<script setup lang="ts">

interface FormModel {
  name?: string | null;
  email?: string | null;
  phone?: string | null;
  address?: string | null;
  resume?: File | null;
}

import { ref } from 'vue';
import { useForm } from 'vee-validate';
import { mixed, object, string } from 'yup';
import { toTypedSchema } from '@vee-validate/yup';

// vle form element components
import VleTextField from '@/components/Core/formElements/VleTextField.vue';
import VleFileInput from '@/components/Core/formElements/VleFileInput.vue';

const customEmailErrorMessage = ref('');
const customEmailValidationInProgress = ref(false);

const supportedFileFormats = ref([
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/pdf'
]);

const maxFileSize = ref(20 * 1024 * 1024);

const { errors, handleSubmit, handleReset, defineField } = useForm<FormModel>({
  validationSchema: toTypedSchema(object(
    {
      name: string()
        .required('Name is required'),

      email: string()
        .email('Invalid email format')
        .required('Email is required')
        .test('customError', (currentValue, context) => {
          return currentValue && currentValue.startsWith('_')
            ? context.createError({ message: `${customEmailErrorMessage.value}`, path: 'email' })
            : true;
        }),

      phone: string()
        .matches(/^(?:\+?61|0)4(?:[ -]?\d){8}$/, 'Phone must be in Aus mobile format')
        .required('Phone is required'),

      address: string()
        .optional(),

      resume: mixed()
        .nullable()
        .required('Resume is required')
        .test('fileSize', 'Maximum file size allowed is 20MB', (file) => {
          return !file || (file && file.size <= maxFileSize.value);
        })
        .test('fileType', 'Allowed file types are .docx, .pdf', (file) => {
          return !file || (file && supportedFileFormats.value.includes(file.type));
        }),
    }))
});

const formBtnLoading = ref(false);

const [name, nameAttrs] = defineField('name');
const [email, emailAttrs] = defineField('email');
const [phone, phoneAttrs] = defineField('phone');
const [address, addressAttrs] = defineField('address');
const [resume, resumeAttrs] = defineField('resume');

const onSubmit = handleSubmit(values => {
  formBtnLoading.value = true;
  console.log('Vee-validate new form > submit clicked');
  console.log(`Name: ${values.name}`);
  console.log(`Email: ${values.email}`);
  console.log(`Phone: ${values.phone}`);
  console.log(`Address: ${values.address}`);
})

const onReset = () => {
  formBtnLoading.value = false;
  console.log('Vee-validate new form > reset clicked')
  handleReset();
}

function verifyEmail() {
  customEmailValidationInProgress.value = true;
  setTimeout(() => {
    new Promise<boolean>((resolve, reject) => {
      const serverError = true;
      customEmailErrorMessage.value = `This is custom error # ${new Date().getTime()}`;
      resolve(serverError);
    }).then((hasError) => {
      email.value = hasError && !email.value?.startsWith('_')
        ? `_${email.value}`
        : email.value;
      customEmailValidationInProgress.value = false;
    });
  }, 2000);
}

</script>

<template>
  <v-expansion-panel class="vee-validate-form-panel">
    <template #title>
      <h4>Examples</h4>
    </template>
    <template #text>
      <form class="pt-5">
        <v-row>
          <v-col 
            sm="12"
            lg="6"
          >
            <vle-text-field
              v-model="name"
              v-bind="nameAttrs"                    
              required
              :error-messages="errors.name"
              label="Name"
            />
          </v-col>

          <v-col 
            sm="12"
            lg="6"
          >
            <vle-text-field
              v-model="email"
              v-bind="emailAttrs"
              :error-messages="errors.email"
              required
              label="Email"
              type="email"
            >
              <template #append-inner>
                <ads-progress-circular
                  v-if="customEmailValidationInProgress"
                  indeterminate
                  size="30"
                />
                <ads-icon 
                  v-else
                  size="large"
                  icon="mdi-magnify"                  
                  @click="verifyEmail"
                />
              </template>              
            </vle-text-field>
          </v-col>

          <v-col 
            sm="12"
            lg="6"
          >
            <vle-text-field
              v-model="phone"
              v-bind="phoneAttrs"
              :error-messages="errors.phone"
              required
              label="Phone"
            />
          </v-col>

          <v-col 
            sm="12"
            lg="6"
          >
            <vle-text-field
              v-model="address"
              v-bind="addressAttrs"
              :error-messages="errors.address"
              label="Address"
            />
          </v-col>

          <v-col
            sm="12"
            lg="6"
          >
            <vle-file-input
              v-model="resume"
              v-bind="resumeAttrs"
              :error-messages="errors.resume"
              prepend-icon=""
              prepend-inner-icon="mdi-paperclip"
              required
              hint=".docx or .pdf file allowed with maximum size of 20MB"
              label="Resume"
            />
          </v-col>
        </v-row>

        <v-row>
          <v-spacer />
          <ads-btn
            prepend-icon="mdi-check"
            color="primary-1"
            :loading="formBtnLoading"
            @click="onSubmit"
          >
            Submit
          </ads-btn>
          &nbsp;&nbsp;
          <ads-btn
            color="primary-1"
            prepend-icon="mdi-reload"
            @click="onReset"
          >
            Reset
          </ads-btn>
        </v-row>
      </form>
    </template>            
  </v-expansion-panel>
</template>