yup validation on multiple values
Asked Answered
F

8

41

I want to validate my form using yup in formik. Suppose I have 4 fields A, B, C, D and they are all strings. How should I write the validation schema if I want to have at least one of the fields is not empty, then that's a valid form? Thanks in advance!

Fane answered 10/9, 2019 at 3:57 Comment(0)
G
53

When using Yup if all normal features fail you, you can use the .test feature, documented here - https://github.com/jquense/yup#mixedtestname-string-message-string--function-test-function-schema

mixed.test(name: string, message: string | function, test: function): Schema

Adds a test function to the validation chain. Tests are run after any object is cast. Many types have some tests built in, but you can create custom ones easily. In order to allow asynchronous custom validations all (or no) tests are run asynchronously. A consequence of this is that test execution order cannot be guaranteed.

For your implementation you will want to write a "test" for each of your 4 fields to make sure one of the 4 are not null.

field1: yup
    .string()
    .test(
      'oneOfRequired',
      'One of Field1, Field2, Field3 or Field4 must be entered',
      function(item) {
        return (this.parent.field1 || this.parent.field2 || this.parent.field3 || this.parent.field4)
      }
    ),
field2: yup
    .string()
    .test(
      'oneOfRequired',
      'One of Field1, Field2, Field3 or Field4 must be entered',
      function(item) {
        return (this.parent.field1 || this.parent.field2 || this.parent.field3 || this.parent.field4)
      }
    ),

etc...

Please note in this case I have not used an arrow function. This is because to use the 'this' context you must use this syntax, this is mentioned in the Yup documentation.

Genteel answered 10/9, 2019 at 13:51 Comment(1)
If you prefer ES6 and want to use arrow functions, checkout this: https://mcmap.net/q/386315/-yup-validation-on-multiple-valuesVane
N
18

There's another possibility if you don't want to add the validation to every field but rather have a "global" error handler for these things.

You'd do something like this:

const schema = yup.object().shape({
    field1: yup.string().required(),
    field2: yup.string().required(),
    field3: yup.string().required(),
    field4: yup.string().required(),
}).test('yourTestCondition', function (value) {
    // your global test code...
})

Nissie answered 14/5, 2020 at 8:17 Comment(1)
In formik you won't get this error unless you'll create a path like so: github.com/formium/formik/issues/2146#issuecomment-720639988Goltz
V
11

If you prefer ES6 and want to use arrow functions, a modified version of Jamie Shepherd's answer would be like this:

field1: yup
    .string()
    .test(
      'oneOfRequired',
      'One of Field1, Field2, Field3 or Field4 must be entered',
      (item,testContext)=>{
        return (testContext.parent.field1 || testContext.parent.field2 || testContext.parent.field3 || testContext.parent.field4)
      }
    ),
field2: yup
    .string()
    .test(
      'oneOfRequired',
      'One of Field1, Field2, Field3 or Field4 must be entered',
      (item,testContext)=> {
        return (testContext.parent.field1 || testContext.parent.field2 || testContext.parent.field3 || testContext.parent.field4)
      }
    ),

For details, official docs.

Vane answered 1/2, 2022 at 19:40 Comment(0)
H
9

There is a solution for what you're searching. Instead of writing a test for each of the elements, you could just write one for the parent. Simulating a Global error.

yup.object({
field1: yup.string(), 
field2: yup.string(),
field3: yup.string(),
field4: yup.string(),
})
.test('global-ok',
      'you do not fulfill the requirements',
      function (value) {
        return CONDITION OVER THE CHILDREN;
      })
      
      

For example, if you don't want to write the error for a series of required elements and just give one type of global error. You could:

    yup.object({
    username: yup.string().required(), 
    password: yup.string().required(),
    email: yup.string().required().test(verify_email),
    })
    .test('global-ok',
          'The data is not correct',
          function (value) {
            return username && password && email;
          })
          
Hindi answered 3/8, 2020 at 22:9 Comment(0)
A
9

This is an example to put things in context

Consider Student name is required only if he/she is from USA and is above 10 years, for this case this is how we can write yup configuration.

Note: multiple fields are used for validation

const formSchema = yup.object({
    age: yup.number(),
    country: yup.string(),
    studentName: yup.string().when(['age', 'country'], {
        is: (age: string, country: string) => {
            if (age > 10) {
                return true // validate and check next condition
            }
            if (country === 'USA') {
                return true // validate and go to then function
            } else return false // for all other cases studentName is not required
        },
        then: (schema) =>
            schema
            .required("Student name is required")
    })

})
Acicula answered 7/7, 2022 at 18:18 Comment(0)
F
2
                email: Yup.string()
                    .when([‘, 'showEmail’, ’anotherField’], {
                        is: (showEmail, anotherField) => {
                            return (showEmail && anotherField);
                        },
                        then: Yup.string().required('Must enter email address')
                    }),

Multiple fields can also be used for validation. The easiest way to handle multiple params

Flyspeck answered 23/4, 2021 at 14:51 Comment(0)
K
0
lazy(value => {
        switch (typeof value) {
          case 'array':
            return array().of(string()).nullable();
          case 'string':
            return string().nullable();
          default:
            return array().of(string()).nullable();
        }
      }),
Knepper answered 1/3, 2021 at 11:50 Comment(0)
D
0

//Why does the validation of the rules field not take effect Which big shot can you help me take a look
const validationSchema = yup.object().shape({
    tableName: yup.string().required(''),
    name: yup.string().required(''),
    cleanTransColumnName: yup.string().required(''),
    mysqlSwitch: yup.string(),
    esSwitch: yup.string(),
    datamaintainConfDTO: yup.object().shape({
        rules: yup.string().when('mysqlSwitch', {
            is: (value) => value === '1',
            then: (schema) => schema.required(''),
            otherwise: (schema) => schema,
        })
    }),
    datamaintainEsIndexConfDTO: yup.object().shape({
        rules: yup.string().when('esSwitch', {
            is: (value) => value === '1',
            then: (schema) => schema.required(''),
            otherwise: (schema) => schema,
        })
    }),
});
Dupree answered 1/9, 2023 at 12:45 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Caldera

© 2022 - 2024 — McMap. All rights reserved.