I have 5 form fields, at last one of which must be populated for validation to succeed. I attempted this, but soon ran into cyclic reference issues:
tag_business: yup.string().when(['tag_cost_centre', 'tag_project_code', 'tag_internal_order', 'tag_business'], {
is: (tag_cost_centre, tag_project_code, tag_internal_order, tag_business) => {
return !(tag_cost_centre, tag_project_code, tag_internal_order, tag_business)
},
then: yup.string().required()
})
What would be the best way to implement this?
That's right but don't include the field itself there. That's why you are getting cycles, your telling yup the field depends on itself, which doesn't make sense
That makes sense, however, I have fixed the issue and am still getting cyclic errors. Here's a reduced test case:
https://runkit.com/nandastone/5a8ceae6d80f770012eb7270
Is yup running validation for sibling fields when they are referenced in when()?
Yeah this is a bit of a gotcha. Fields that depend on each other need to sorted so they are "constructed" in the right order, e.g. if depend on field A in field B, yup needs to cast and coerce the value in field A before it's handed to B. What's happening here tho is you are only adding a validation in the condition, so there isn't _actually_ any need to order anything validation happens after everything is constructed already. because of the flexibility and programmatic nature of yup it can't distinguish between those two cases.
There is an ugly escape hatch: using the shape() method directly that tells yup to ignore the ordering for a pair of edges: https://runkit.com/5908cecfe97ebf0012f2e3c9/5a8d87d03a470f00122666d8
I don't quite understand the low-level reason you've explained, but appreciate you taking the time to help.
Your example worked well, until I extended the validation to depend on more than one other field:
https://runkit.com/nandastone/5a8e0bf15ae96a0012e235a7
Thoughts?
var schema = yup.object().shape({
a: yup.string().when(['b', 'c'], {
is: (b, c) => !b && !c,
then: yup.string().required()
}),
b: yup.string().when(['a', 'c'], {
is: (a, c) => !a && !c,
then: yup.string().required()
}),
c: yup.string().when(['a', 'b'], {
is: (a, b) => !a && !b,
then: yup.string().required()
})
}, [['a', 'b'], ['a', 'c'], ['b','c']]) // <-- HERE!!!!!!!!
Has there been a resolution to this? I am experiencing the same thing. I upgraded the code a bit in one of the links to this and get cyclic dependency issue
var schema = yup.object().shape({
a: yup.string().when(['b', 'c', 'd'], {
is: (b, c, d) => !b && !c && !d,
then: yup.string().required()
}),
b: yup.string().when(['a', 'c', 'd'], {
is: (a, c, d) => !a && !c && !d,
then: yup.string().required()
}),
c: yup.string().when(['a', 'b', 'd'], {
is: (a, b, d) => !a && !b && !d,
then: yup.string().required()
}),
d: yup.string().when(['a', 'b', 'c'], {
is: (a, b, c) => !a && !b && !c,
then: yup.string().required()
})
}, [['a', 'b', 'c'], ['b', 'c', 'd'], ['a','c', 'd'], ['a','b','d']]) // <-- HERE!!!!!!!!
Hi @pmonty the issue in your last snippet I believe is because you aren't properly enumerating the list of 'edges' in your validation schema.
Pretty much in the array of combinations at the end of the .shape(...) call you need to list all the pair-wise (2-tuples) combinations - in your example you have triples (i.e. ['a','b','c']).
Because you have 4 fields, you'd expect to have 6 pairs in your array.
Anyway, I did this and it worked fine for me.
I have an array of objects, and for each object, I have dependent fields. My code:
const schema = Yup.object().shape({
colleagues: Yup.array().of(
Yup.object().shape({
name: Yup.string().when('role', {
is: role => role.length > 0,
then: Yup.string().required('Colleague name is required.')
}),
role: Yup.string().when('name', {
is: name => name.length > 0,
then: Yup.string().required('Colleague role is required.')
})
}, ['name', 'role'])
),
email: Yup.string()
.email('Email is not valid.')
.required('Email is required.'),
});
When I remove the when validation on name or role, the validation works perfectly, and I receive errors for all fields. However, when I include both, and enumerate the dependencies of the array objects, I don't receive errors for anything, including the top-level email field. Since these are objects inside of an array, am I specifying the paths incorrectly?
The solutions here seem straightforward enough, so I'm sure I'm missing something small. Any help would be much appreciated.
It turns out that role and name were coming back as undefined, so the validation never got to the conditional check of the object in the array. Changing the conditional check to role && role.length > 0 seems to have resolved this issue.
@jmpolitzer Thanks, it saved me!
var schema = yup.object().shape({ a: yup.string().when(['b', 'c'], { is: (b, c) => !b && !c, then: yup.string().required() }), b: yup.string().when(['a', 'c'], { is: (a, c) => !a && !c, then: yup.string().required() }), c: yup.string().when(['a', 'b'], { is: (a, b) => !a && !b, then: yup.string().required() }) }, [['a', 'b'], ['a', 'c'], ['b','c']]) // <-- HERE!!!!!!!!
This does not work anymore! Turns out that is function now only accepts 1 param. So now I can validate at most 2 fields of which 1 is required.
So the type of is function is now boolean | ((value: any) => boolean). Any reason why this has changed? @jquense
For now I got it work by changing the type to boolean | ((...value: any) => boolean). Any chance this will be fixed in a coming release?
@loolooii, I ran into the same TypeScript issue, and it looks like you just have to upgrade @types/yup to v0.26.14 or later. The issue was fixed in https://github.com/DefinitelyTyped/DefinitelyTyped/pull/35885.
instead of using examples above, i just created a 'ghost' field in yup validation schema that checks specified fields, alot less code and no cyclic errors
yup.object().shape({
a: yup.string(),
b: yup.string(),
AorB: yup.bool().when(['a', 'b'], {
is: (a, b) => (!a && !b) || (!!a && !!b),
then: yup.bool().required('some error msg'),
otherwise: yup.bool()
})
})
This will generate an error and prevent submit.
When using Formik Fields just remember to include additional errors from this 'ghost' field, like:
<Field
error={!!(errors && errors.a || (errors as any).AorB)}
helperText={errors && errors.b || (errors as any).AorB)}
/>
(errors as any) because TypeScript will not see this error (field is not in initialValues)
I only want to check heightUnit if heightValue is > 0.
```
heightValue: Yup.number().positive().when(['heightUnit'], {
is: val => !!val,
then: Yup.number(),
otherwise: Yup.number().negative('Unit is required')
})
https://github.com/jquense/yup/issues/176#issuecomment-442977916 This comment save my day !
#176 (comment) This comment save my day !
Could you please provide me an example? I couldn't get it working :(
This works for me:
const phoneRegex = /^\+([0-9]{1,3})*\.([0-9]{5,16})$/;
yup.object().shape({
phoneNumber: yup.string()
.matches(phoneRegex)
.when('mobileNumber', {
is: val => !!val,
then: yup.string(),
otherwise: yup.string()required('phone or mobile number required')
}),
mobileNumber: yup.string()
.matches(phoneRegex)
.when('phoneNumber', {
is: val => !!val,
then: yup.string(),
otherwise: yup.string().required('phone or mobile number required')
})
}, [['mobileNumber', 'phoneNumber']]);
Hi @pmonty the issue in your last snippet I believe is because you aren't properly enumerating the list of 'edges' in your validation schema.
Pretty much in the array of combinations at the end of the
.shape(...)call you need to list all the pair-wise (2-tuples) combinations - in your example you have triples (i.e.['a','b','c']).
Because you have 4 fields, you'd expect to have 6 pairs in your array.Anyway, I did this and it worked fine for me.
this is working for me
const AddusersSchema= object().shape({
lastName: string().when(['firstname', 'emailId', 'userType1'], {
is: (firstname,emailId,userType1 ) => firstname || emailid || userType1,
then: string().required()
}),
firstname: string().when(['lastName', 'userType1','emailId'], {
is: ( lastsame, emailId , userType1) => lastsame || emailId || userType1,
then: string().required()
}),
emailId: string().when(['firstname', 'lastName', 'userType1'], {
is: (firstname,lastsame1, userType1) => firstname || lastsame1 || userType1,
then: string().required()
}).matches(/(?=.*[@$!%*#?&])/, 'Please enter valid email.'),
userType1: string().when(['firstname','lastname','emailId'], {
is: ( lastsame, emailId , userType1) => lastsame || emailId || userType1,
then: string().required()
})
},[['firstname','lastName'], ['firstname', 'emailId'] ,
[ 'userType1','emailId'] , ['lastName', 'userType1'],
['lastName', 'emailId'], ['firstname','userType1']]);
var schema = yup.object().shape({ a: yup.string().when(['b', 'c'], { is: (b, c) => !b && !c, then: yup.string().required() }), b: yup.string().when(['a', 'c'], { is: (a, c) => !a && !c, then: yup.string().required() }), c: yup.string().when(['a', 'b'], { is: (a, b) => !a && !b, then: yup.string().required() }) }, [['a', 'b'], ['a', 'c'], ['b','c']]) // <-- HERE!!!!!!!!
it is throwing Uncaught Error: Cyclic dependency, the node was: "b". Someone, Please, help me with the working code. I required only one of the fields
Is this still a problem or is there a documented solution?
Most helpful comment