'VeeValidate and YUP async lazy email validation sending requests for every input in form on background
I'm trying to add lazy e-mail unique validator to my registration form. It works but when i type something in other fields (including e-mail field also) unique validator keeps running and sending request to server repeatedly. I think form level validation causes it but i couldn't find a way to turn off it. How to prevent validating email
field in form level for annoying validation requests?
Here is the script setup:
<script setup lang='ts'>
// E-mail unique validator (adding method to YUP's string)
addMethod(string, 'unique', function unique(message: string) {
return this.test('unique', message, function (value: string) {
const { path, createError } = this;
return new Promise((resolve, reject) => {
EventService.checkEmailUniqueness(value).then(() => {
resolve(true)
}).catch(() => {
reject(createError({ path, message }))
})
});
})
})
// YUP validation schema
const userLoginSchema = object({
firstname: string().required(t('fieldRequired')),
lastname: string().required(t('fieldRequired')),
email: string().email(t('emailRequired')).unique(t('emailInUse')).required(t('fieldRequired')),
password: string().required(t('fieldRequired')).min(8, t('minPasswordLength', {min: 8})),
password2:
string().required(t('fieldRequired')).min(8, t('minPasswordLength', {min: 8})).oneOf([refy('password')],
t('passwordNotMatch')),
})
// Form and fields
const { handleSubmit, errors } = useForm({
validationSchema: userLoginSchema,
validateOnMount: false,
});
const { value: firstname } = useField('firstname')
const { value: lastname } = useField('lastname')
const { value: email, handleChange } = useField('email')
const { value: password } = useField('password')
const { value: password2 } = useField('password2')
</script>
Form template (built using vuetify):
<template lang='pug'>
v-card(width='300').mx-auto
v-card-text
v-form(@submit.prevent='register')
v-text-field(v-model='firstname' :label='$t("firstname")'
:error-messages='errors.firstname' required)
v-text-field(v-model='lastname' :label='$t("lastname")'
:error-messages='errors.lastname' required)
v-text-field(:model-value='email' :label='$t("email")'
type='email' :error-messages='errors.email' @change='handleChange' required)
v-text-field(v-model='password' :label='$t("password")'
:append-inner-icon='showPassword ? "mdi-eye": "mdi-eye-off"'
:type='showPassword ? "text": "password"'
@click:append-inner='showPassword = !showPassword' :error-messages='errors.password'
required)
v-text-field(v-model='password2' :label='$t("repeatPassword")'
:append-inner-icon='showPassword ? "mdi-eye": "mdi-eye-off"'
:type='showPassword ? "text": "password"'
@click:append-inner='showPassword = !showPassword' :error-messages='errors.password2'
required)
div.text-body-1.text-red {{ err }}
v-btn(v-if='!loading' color='success' variant='contained-text'
type='submit').mt-5 {{ $t('register') }}
v-progress-circular(v-if='loading' color='info' indeterminate="").mt-5
</template>
Solution 1:[1]
I couldn't figure out how to disable repeated validation on email field but the answer here let me chain my validations (normally validations run in parallel) and get rid of ridiculous requests.
Here is the edited version of schema above, hope it helps:
const sequence = <T>(
name: string,
...schemas: ReadonlyArray<BaseSchema>
): TestConfig<T> => {
return {
name,
test: async (value, context) => {
try {
for (const schema of schemas) {
await schema.validate(value, { context })
}
} catch (e) {
if (e instanceof ValidationError) {
return context.createError({ message: e.message })
}
throw e
}
return true
},
}
}
const userLoginSchema = object({
firstname: string().required(t('fieldRequired')),
lastname: string().required(t('fieldRequired')),
email: string().test(
sequence('email',
string().required(t('fieldRequired')).email(t('emailRequired')),
string().test( // You can also write your custom validations via addMethod like in original post
{
name: 'unique',
message: t('emailInUse'),
test: async (value: string) => {
EventService.checkEmailUniqueness(value).then((response) => {
return response.data.is_available
}).catch((e) => { throw e })
}
}
)
)
),
password:
string().matches(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d).{8,}$/,
t('passwordMatch')).required(t('fieldRequired')).min(8,
t('minPasswordLength',
{min: 8})),
password2:
string().required(t('fieldRequired')).oneOf([refy('password')], t('passwordNotMatch')),
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ghros |