Yup: Array of Object Conditional Validation - reactjs

I have a data structure like this:
{
isReceivesSalary: true,
boardMembers: [
{
name: 'David',
country: 'USA',
additionalProperty: {
isReceivingSalary: true
}
},
{
name: 'Dave',
country: 'France',
additionalProperty: {
isReceivingSalary: false
}
}
]
}
When isReceivesSalary is checked then at least one of the boardMembers should have
property isReceivingSalary should be checked.
Below is my validation schema which is not working.
const boardMembersValidation ={
isReceivesSalary: yup.boolean().required(),
boardMembers: yup.array(yup.object.shape({
name: yup.string().required(),
country: yup.string().required(),
additionalProperty: yup.object().shape({
isReceivingSalary: yup.boolean()
})
}))
};
Is it possible to validate at least one of the boardMember should be receiving salary when isReceivesSalary is true?
Also add validation message.

Related

Using Yup and Formik, how to get index number when validating conditional property inside of fields array?

I need to show and validate "hourlyCommitment" field ONLY when "commitment" field has a value of "3". The problem is, I can't seem to access the "current index" of the array field, so the validation doesn't work. Any ideas would be appreciated
professionals: Yup.array().of(
Yup.object().shape({
talent: Yup.number().required(translates.FormsErrorsRequired),
seniority: Yup.number().required(translates.FormsErrorsRequired),
commitment: Yup.number().required(translates.FormsErrorsRequired),
hourlyCommitment: Yup.number()
.nullable()
.when('professionals[currentIndex].commitment', {
is: '3',
then: Yup.number().required(translates.FormsErrorsRequired),
}),
quantity: Yup.number().required(translates.FormsErrorsRequired),
skills: Yup.array().of(
Yup.object().shape({
value: Yup.string(),
label: Yup.string(),
})
),
})
),
Seems I solved it:
professionals: Yup.array().of(
Yup.object().shape({
talent: Yup.number().required(translates.FormsErrorsRequired),
seniority: Yup.number().required(translates.FormsErrorsRequired),
commitment: Yup.number().required(translates.FormsErrorsRequired),
hourlyCommitment: Yup.number().test({
name: 'hourlyCommitment',
exclusive: true,
message: translates.FormsErrorsRequired,
test: (value, context) => {
return !!value || context.parent.commitment !== 3;
},
}),
quantity: Yup.number().required(translates.FormsErrorsRequired),
skills: Yup.array().of(
Yup.object().shape({
value: Yup.string(),
label: Yup.string(),
})
),
})
),
Why don't you just use commitment in initial values?
Are they different values with professionals[current].commitment?
If yes, then define a variable called currentCommitment and set it to professionals[current].commitment, then use it in validation

Yup validation for object under array

I am using Formik + Yup for form creation with validation. I have following initial value.
const initialValue = {
title: '',
schedules: [
{
maxGame: '',
bookingDuration: [],
reservationSlots: [
{
from: null,
to: null,
increment: '',
pricePerHours: [
{
guest: '',
price: ''
}
],
pricePerGame: [
{
type: 'Child',
price: ''
}
]
}
]
}
]
};
My current yup schema:
const schema = {Yup.object().shape({
title: Yup.string()
.min(2, 'Must be 2 characters or more')
.required('Title is required'),
schedules: Yup.array().of(
Yup.object().shape({
bookingDuration: Yup.array().when('maxGame', {
is: '',
then: Yup.array().min(1, 'Booking Duration is required'),
otherwise: Yup.array()
}),
maxGame: Yup.number().when('bookingDuration', {
is: (bookingDuration) => bookingDuration.length === 0,
then: Yup.number()
.min(1, 'Max Game must be greater than zero')
.required('Max Game is required either fill booking duration'),
otherwise: Yup.number()
}),
reservationSlots: Yup.array().of(
Yup.object().shape({
from: Yup.date().typeError('Invalid Time'),
to: Yup.date().typeError('Invalid Time'),
increment: Yup.string().required('Time Increment is required'),
pricePerGame: Yup.array().of(
Yup.object().shape({
type: Yup.string(),
price: Yup.string()
.when(['type'], {
is: (type) => type,
then: Yup.string().required('Price is required'),
}),
})
),
},
)
},
[['bookingDuration', 'maxGame']])
)
})
}
I want following validation:
Either one of bookingDuration and maxGame is required.
If bookingDuration.length > 0 then price of pricePerHours should be required.
If price of pricePerHours is filled then bookingDuration should be required.
If maxGame !== '' then price of pricePerGame should be required.
If price of pricePerGame is filled then maxGame should be required.
I am able to apply no. 1 validation but for rest unable to apply validation. Can you please help me on this.

react hook forms - disappearing default values

I have been having some problems with react-hook-form and disappearing defaultValues
So I initiate the useForm with default values, and then get async user location from our API. I want to pre-fill the country based on our user data.
unfortunatelly, after I do that, loose all default values inside payment prop.
why is that happening and how can I fix it?
I tried to find some solutions but none seem to help me (setTimeout, useMemo on defaultValues etc)
const form = useForm<CheckoutFormSteps>({
defaultValues: {
team: {
id: null,
icon: 'url',
name: 'someName',
},
payment: {
address1: '',
address2: '',
city: '',
companyName: '',
paymentMethod: PaymentMethod.CreditCard,
country: '',
firstName: '',
lastName: '',
phone: '',
postalCode: '',
state: '',
vat_number: '',
},
},
});
useEffect(() => {
// some async logic to get user's country
form.setValue('payment.country', 'US');
// also tried setTimeout(() => form.setValue('payment.country', 'AU'));
}, []);
then the getValues look like
{
team: {
id: null,
icon: 'url',
name: 'someName',
},
payment: {
country: 'US',
} // all other payment props are gone
}
Here you go with a solution
const form = useForm<CheckoutFormSteps>({
defaultValues: {
team: {
id: null,
icon: 'url',
name: 'someName',
},
payment: {
address1: '',
address2: '',
city: '',
companyName: '',
paymentMethod: PaymentMethod.CreditCard,
country: '',
firstName: '',
lastName: '',
phone: '',
postalCode: '',
state: '',
vat_number: '',
},
},
});
useEffect(() => {
// some async logic to get user's country
form.setValue({
...form,
payment: {
...form.payment,
country: "US"
}
});
// also tried setTimeout(() => form.setValue('payment.country', 'AU'));
}, []);
Whenever you are setting the value, you need to retain the old value as value. For the same reason please use spread operator.

Conditional Yup Validation is not working

I have formik form with below initialValues
const initialValues1 = {
people: [
{
id: Math.random(),
email: "",
isNewUser: true,
profile: {
firstName: "",
lastName: "",
}
}
]
};
I want to validate firstName and lastName only when is isNewUser is true by Yup I am trying below but it is not working. How can validate conditionally in Formik Yup
people: Yup.array().of(
Yup.object().shape({
isNewUser: Yup.boolean(),
profile: Yup.object().shape({
firstName: Yup
.string()
.when('isNewUser', {
is: true,
then: Yup.string().required("First name is required")
}),
})
})
)
formatted code in IDE
people: Yup.array().of(
Yup.object({
isNewUser: Yup.boolean(),
profile: Yup.object().when('isNewUser', {
is: true,
then: Yup.object({
firstName: Yup.string().required('First name is required'),
})
})
})
);
isNewUser is sibling of profile attribute, so we can use it in when for defining profile schema not it's child(first_name) schema directly.
you can also specify else part using otherwise key
when({ 'attrbute', is: 'func or value', then: 'schema if is true', otherwise: 'schema if is false'})
As per docs ,
Adjust the schema based on a sibling or sibling children fields. You
can provide an object literal where the key is is value or a matcher
function, then provides the true schema and/or otherwise for the
failure condition.
So move the isNewUser to reflect as sibling. Like this:
const initialValues1 = {
people: [
{
id: Math.random(),
email: "",
//isNewUser: true, // <--------------- remove this
profile: {
firstName: "",
lastName: "",
isNewUser: true // <---------------- here.
}
}
]
};

Formik Yup Validation of array of things

Here's the schema for my data. It includes a few top-level properties but also some nested inputs that are part both a collection things, and an object thing2. An example of the input name for the first input of the second item in the things array is: things[0].inputs[1].value. Formik seems to be attaching the values via the names correctly but the validation doesn't seem to work. Any idea where I'm going wrong?
const schema = {
name: "", // string - required
feed_id: "", //string - required
things: [
{
inputs: [
// zero, one, or more of the following
// They all require a value, but it can really be any type,
{
name: "InputName",
value: "StringInput" // String - required
},
{
name: "InputName",
value: true // boolean - required
},
{
name: "InputName",
value: 5 // number - required
}
]
}
],
thingB: {
inputs: [
// zero, one, or more of the following
{
name: "InputName",
value: "StringInput" // String - required
},
{
name: "InputName",
value: true // boolean - required
},
{
name: "InputName",
value: 5 // number - required
}
]
}
};
And here's my attempt at a YUP validation for it
const inputValidationSchema = yup.array().of(
yup.object().shape({
value: yup
.mixed()
.required()
})
);
const validationSchema = yup.object().shape({
name: yup.string().required(),
feed_id: yup.string().required(),
config: yup.object().shape({
things: yup
.array()
.of(yup.object().shape({ inputs: inputValidationSchema })),
thingB: yup.mixed().oneOf([
yup.object().shape({
inputs: inputValidationSchema
}),
null
])
})
});
It doesn't seem to work and properly attach the errors to the form.

Resources