How to validate nested object with joi-browser - reactjs

I am trying to validate an object that has a another nested object but no luck so far. Context:
class ContactForm extends Component {
state = {
contact: {
name: { firstName: "", lastName: "" },
email: "",
subject: "",
message: ""
},
errors: {}
};
schema = {
name: Joi.object({ firstName: Joi.string(), lastName: Joi.string() })
.required()
.label("Name"),
email: Joi.string()
.email()
.required()
.label("Email"),
subject: Joi.string()
.min(5)
.max(50)
.required()
.label("Subject"),
message: Joi.string()
.min(5)
.max(50)
.required()
.label("Message")
};
}
I've tried reading the joi documentation but I couldn't find an example similar to my situation.The form looks like this by the way:
Notice that I added a label to the name object in my scheme, which is not showing in the error message as the other properties in my schema. Please let me know if you need more context than this to edit my post, I'm stuck here :/

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: Array of Object Conditional Validation

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.

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.

React yup validation with and react-hook-form, array of different type of objects depends from one of object attribute

I have a form, where user can add with no limit ( add shareholder ).
Shareholder can be 2 different types ( NATURAL / LEGAL )
here is interface for thouse objects:
export interface shareholderNatural {
type: 'NATURAL';
firstName: string;
lastName: string;
personalCode: string;
sharePart: number;
politicallyExposed: boolean;
country: string;
position: string;
}
export interface shareholderLegal {
type: 'LEGAL';
companyName: string;
companyCode: string;
sharePart: number;
politicallyExposed: boolean;
country: string;
position: string;
}
I need validation for:
firstName, lastName, personalCode, sharePart if type is NATURAL
companyName, companyCode, sharePart if type is NATURAL
Attribut type is set by the select from the component state when user click opn add another sharehoilder button (useState)
I tryed to do it with Yup.array().when( 'key', {})
const validationSchema = Yup.object().shape({
shareholders: Yup.array().when('type', {
is: 'NATURAL',
then: Yup.array().of(
Yup.object().shape({
firstName: Yup.string().required(t('')),
lastName: Yup.string().required(t('')),
personalCode: Yup.string().required(t('')),
sharePart: Yup.number().required(t('')),
}),
),
otherwise: Yup.array().of(
Yup.object().shape({
companyName: Yup.string().required(t('')),
companyCode: Yup.string().required(t('')),
sharePart: Yup.number().required(t('')),
}),
),
}),
});
But in this case key attribute should be outside ...
Any help here ?
Thank you.
You can check every field separately. Just replace type with needed field:
const validationSchema = Yup.object().shape({
shareholders: Yup.array().of(
Yup.object().shape({
firstName: Yup.string().when('type', {
is: 'NATURAL',
then: Yup.string().required(t(''))
}),
lastName: Yup.string().when('type', {
is: 'NATURAL',
then: Yup.string().required(t(''))
}),
personalCode: Yup.string().when('type', {
is: 'NATURAL',
then: Yup.string().required(t(''))
}),
sharePart: Yup.number().required(t('')),
companyName: Yup.string().when('type', {
is: 'LEGAL',
then: Yup.string().required(t(''))
}),
companyCode: Yup.string().when('type', {
is: 'LEGAL',
then: Yup.string().required(t(''))
}),
})
)
)
};

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.
}
}
]
};

Resources