reectrix
reectrix

Reputation: 8629

Conditional Validation in Yup

I have an email field that only gets shown if a checkbox is selected (boolean value is true). When the form get submitted, I only what this field to be required if the checkbox is checked (boolean is true).

This is what I've tried so far:

const validationSchema = yup.object().shape({
   email: yup
         .string()
         .email()
         .label('Email')
         .when('showEmail', {
             is: true,
             then: yup.string().required('Must enter email address'),
         }),
    })

I've tried several other variations, but I get errors from Formik and Yup:

Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
    at yupToFormErrors (formik.es6.js:6198)
    at formik.es6.js:5933
    at <anonymous>
yupToFormErrors @ formik.es6.js:6198

And I get validation errors from Yup as well. What am I doing wrong?

Upvotes: 192

Views: 380741

Answers (13)

Abi Ji
Abi Ji

Reputation: 313

Using Yup ^1.6 without Typescript overload complaining

validationSchema={yup.object().shape({
    showEmail: yup.boolean(),
    email: yup
      .string()
      .email()
      .when("showEmail", {
        is: true,
        then(schema): {
               return schema.required("Must enter email address");
          }
      })
  })
}

Upvotes: 0

Aliansyah Firdaus
Aliansyah Firdaus

Reputation: 181

for typescript using this

reasonContractNotDone: yup.string().when('isContractDone', {
  is: false,
  then: (schema) => schema.required('Must enter email address')
}),

NOT like below!

reasonContractNotDone: yup.string().when('isContractDone', {
  is: false,
  then: yup.string().required("Must enter email address")
}),

Upvotes: 13

hard_fi
hard_fi

Reputation: 1

If the condition is supposed to come from the outside of the form (for example you have more generic approach to creating validations, and have more abstraction above), you can also do something like this:

export const getNameFieldValidation = (isRequired?: boolean) => {
  const fieldSchema = yup
    .string()
    .transform((value) => stringParser.parseToNoExtraSpaces(value));

  if (isRequired) {
    return fieldSchema.concat(yup.string().required());
  }

  return fieldSchema;
};

Upvotes: 0

Georgy Martynovich
Georgy Martynovich

Reputation: 533

Attention anyone using Yup v1 and upper. v1.2 in my case. According to the official docs you have to do (schema) => ... in you conditions

Official Docs:
For schema with dynamic components (references, lazy, or conditions), describe requires more context to accurately return the schema description. In these cases provide options

import { ref, object, string, boolean } from 'yup';

let schema = object({
  isBig: boolean(),
  count: number().when('isBig', {
    is: true,
    then: (schema) => schema.min(5),
    otherwise: (schema) => schema.min(0),
  }),
});

schema.describe({ value: { isBig: true } });

Upvotes: 16

hansaplast
hansaplast

Reputation: 11593

Checking for a specific value without using the function notation:

If select choice has value date, then input field date is required:

availableDate: yup.string().when('choice', {
    is: (v) => v === 'date',
    then: (schema) => schema.required('date is required')
})

Upvotes: 2

jaredpalmer
jaredpalmer

Reputation: 1919

Formik author here...

To make Yup.when work properly, you would have to add showEmail to initialValues and to your Yup schema shape.

In general, when using validationSchema, it is best practices to ensure that all of your form's fields have initial values so that Yup can see them immediately.

The result would look like:

<Formik 
  initialValues={{ email: '', showEmail: false }}
  validationSchema={Yup.object().shape({
    showEmail: Yup.boolean(),
    email: Yup
      .string()
      .email()
      .when("showEmail", {
        is: true,
        then: Yup.string().required("Must enter email address")
      })
  })
}

/>

Upvotes: 90

vishal sharma
vishal sharma

Reputation: 1

This Code Works For ME Try To use It

const validation = () => {
   try {
    let userSchema = Yup.object().shape({
    date: Yup.string().required(),
    city: Yup.string().required(),
    gender: Yup.string().required(),
    email: Yup.string()
      .matches(
        /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|. 
   (".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA- 
   Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
      )
      .nullable(true),
    userName: Yup.string().min(3),
  });
  userSchema.validateSync({
    email: email,
    userName: userName,
    gender: gender.name,
    city: city.name,
    date: date,
   });
 } catch (error) {
  console.log(error);
   setError({
     userName: '',
     email: '',
     gender: '',
     city: '',
     date: '',
     [error.path]: error.name,
    });
   }
 };

Upvotes: 0

sridhar..
sridhar..

Reputation: 2133

You can even use a function for complex cases . Function case helps for complex validations

validationSchema={yup.object().shape({
    showEmail: yup.boolean(),
    email: yup
      .string()
      .email()
      .when("showEmail", (showEmail, schema) => {
        if(showEmail)
          return schema.required("Must enter email address")
        return schema
      })
  })
}

Upvotes: 49

Samira
Samira

Reputation: 2753

it works for me very well :

   Yup.object().shape({
    voyageStartDate:Yup.date(),
    voyageEndDate:Yup.date()
        .when(
            'voyageStartDate',
            (voyageStartDate, schema) => (moment(voyageStartDate).isValid() ? schema.min(voyageStartDate) : schema),
        ),
})

Upvotes: 4

Ozal  Zarbaliyev
Ozal Zarbaliyev

Reputation: 638

I use yup with vee-validate

vee-validate

here is the sample code from project

const schema = yup.object({
    first_name: yup.string().required().max(45).label('Name'),
    last_name: yup.string().required().max(45).label('Last name'),
    email: yup.string().email().required().max(255).label('Email'),
    self_user: yup.boolean(),
    company_id: yup.number()
        .when('self_user', {
            is: false,
            then: yup.number().required()
        })
})
const { validate, resetForm } = useForm({
    validationSchema: schema,
    initialValues: {
        self_user: true
    }
})

const {
    value: self_user
} = useField('self_user')
const handleSelfUserChange = () => {
    self_user.value = !self_user.value
}

Upvotes: 1

pareshm
pareshm

Reputation: 4984

email: Yup.string()
    .when(['showEmail', 'anotherField'], {
        is: (showEmail, anotherField) => {
            return (showEmail && anotherField);
        },
        then: Yup.string().required('Must enter email address')
    }),

Upvotes: 20

Eric Tan
Eric Tan

Reputation: 1463

Totally agree with @João Cunha's answer. Just a supplement for the use case of Radio button.

When we use radio button as condition, we can check value of string instead of boolean. e.g. is: 'Phone'

const ValidationSchema = Yup.object().shape({
  // This is the radio button.
  preferredContact: Yup.string()
    .required('Preferred contact is required.'),
  // This is the input field.
  contactPhone: Yup.string()
    .when('preferredContact', {
      is: 'Phone',
      then: Yup.string()
        .required('Phone number is required.'),
    }),
  // This is another input field.
  contactEmail: Yup.string()
    .when('preferredContact', {
      is: 'Email',
      then: Yup.string()
        .email('Please use a valid email address.')
        .required('Email address is required.'),
    }),

});

This the radio button written in ReactJS, onChange method is the key to trigger the condition checking.

<label>
  <input
    name="preferredContact" type="radio" value="Email"
    checked={this.state.preferredContact == 'Email'}
    onChange={() => this.handleRadioButtonChange('Email', setFieldValue)}
  />
  Email
</label>
<label>
  <input
    name="preferredContact" type="radio" value="Phone"
    checked={this.state.preferredContact == 'Phone'}
    onChange={() => this.handleRadioButtonChange('Phone', setFieldValue)}
  />
  Phone
</label>

And here's the callback function when radio button get changed. if we are using Formik, setFieldValue is the way to go.

handleRadioButtonChange(value, setFieldValue) {
  this.setState({'preferredContact': value});
  setFieldValue('preferredContact', value);
}

Upvotes: 26

Jo&#227;o Cunha
Jo&#227;o Cunha

Reputation: 10307

You probably aren't defining a validation rule for the showEmail field.

I've done a CodeSandox to test it out and as soon as I added:

showEmail: yup.boolean()

The form started validation correctly and no error was thrown.

This is the url: https://codesandbox.io/s/74z4px0k8q

And for future this was the correct validation schema:

validationSchema={yup.object().shape({
    showEmail: yup.boolean(),
    email: yup
      .string()
      .email()
      .when("showEmail", {
        is: true,
        then: yup.string().required("Must enter email address")
      })
  })
}

Upvotes: 212

Related Questions