I'm using Formik's FieldArray
to add objects dynamically to an array, rendering additional form elements as objects are push()
ed to the array.
My schema looks like this:
const EMAIL_SCHEMA = Yup.object().shape({
address: Yup.string().email().required( 'E-mail address is required.' ),
isPreferredContact: Yup.boolean()
})
const SCHEMA = Yup.object().shape({
emails: Yup.array()
.of( EMAIL_SCHEMA )
.ensure()
.compact( v => !v.address )
.required( 'At least one e-mail address is required.' )
})
For each e-mail input, there is a corresponding checkbox to indicate if it is the preferred contact e-mail address. No e-mail addresses are required to be marked as preferred.
What I would like to do is validate that the array contains at most one object where isPreferredContact
is true
. If there are 3 e-mail objects in the array and isPreferredContact
is false
for all of them, that's a valid state. That is to say:
let values = [
{address: '[email protected]', isPreferredContact: false},
{address: '[email protected]', isPreferredContact: false},
{address: '[email protected]', isPreferredContact: false}
] // OK
let values = [
{address: '[email protected]', isPreferredContact: true},
{address: '[email protected]', isPreferredContact: false},
{address: '[email protected]', isPreferredContact: false}
] // OK
let values = [
{address: '[email protected]', isPreferredContact: true},
{address: '[email protected]', isPreferredContact: true},
{address: '[email protected]', isPreferredContact: false}
] // Invalid
I see this answer
Yup: deep validation in array of objects
shows that the compact()
method can be used to validate for at least one, because if after removing "falsy" values from the array, the array is empty, then it's easy to treat the schema key as invalid.
There's nothing I can see, though, for validating that the array contains at most one object with a property = value predicate.
Is there a way to do this?
Solution: const yup = require('yup') const { setLocale } = yup setLocale({ mixed: { notType: 'the ${path} is obligatory', required: 'the field ${path} is obligatory', oneOf: 'the field ${path} must have one of the following values: ${values}' } }) const myNameSchema = yup. object(). shape({ first_name: yup.
The validation library Yup allows you to pass in values, which aren't validated themselves, to aid in validation using a context. This is useful for making sure a reference to another object is valid, by passing in an instance of the other object.
up is a JavaScript object schema validator and object parser. It is not required to use Formik.
After poking through Yup's issues in GitHub, staring at the API docs (the doc for addMethod
is really terrible), and testing in Code Sandbox, I found that this works:
Yup.addMethod(Yup.array, 'atMostOne', function(args) {
const { message, predicate } = args
return this.test('atMostOne', message, function(list) {
// If there are 2+ elements after filtering, we know atMostOne must be false.
return list.filter(predicate).length < 2
})
})
The predicate, obviously, is a function that takes an element of the array and performs a test on it that returns a boolean
.
For an array of scalar values, this is as simple as el => el === value
. For an array of objects, it would be el => el.property === value
or el[property] === value
.
Hope this helps anybody else curious about this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With