Yup cyclic dependency: Two fields mutually requiring each other - javascript

I have the following Yup validation schema
const validationSchema = Yup.object().shape({
name: Yup.string(),
services: Yup.array(Yup.string().oneOf(SERVICES, "Invalid service!")),
locations: Yup.array(Yup.string().oneOf(LOCATIONS, "Invalid location!")),
distance: Yup.number()
.typeError("Invalid distance!")
.positive("Invalid distance!")
.when("userFormattedAddress", {
is: (val) => !!val,
then: Yup.number().required(),
otherwise: Yup.number(),
}),
userFormattedAddress: Yup.string("Invalid user location!").when("distance", {
is: (val) => !!val,
then: Yup.string().required(),
otherwise: Yup.string(),
}),
userCoordinates: Yup.array(
Yup.number("Invalid user location!").positive("Invalid user location!")
),
});
The desired behaviour is that when a distance is entered, the user must enter an address, and when a user enters an address , they must specify a distance too. However, I run into a cyclic dependency... Any thoughts? Thanks!

Okay, I found the answer:
const validationSchema = Yup.object().shape({
name: Yup.string(),
services: Yup.array(Yup.string().oneOf(SERVICES, "Invalid service!")),
locations: Yup.array(Yup.string().oneOf(LOCATIONS, "Invalid location!")),
distance: Yup.number()
.typeError("Invalid distance!")
.positive("Invalid distance!")
.when("userFormattedAddress", {
is: (val) => !!val,
then: Yup.number().required(),
otherwise: Yup.number(),
}),
userFormattedAddress: Yup.string("Invalid user location!").when("distance", {
is: (val) => !!val,
then: Yup.string().required(),
otherwise: Yup.string(),
}),
userCoordinates: Yup.array(
Yup.number("Invalid user location!").positive("Invalid user location!")
),
}, ["distance", "userFormattedAddress"]);
where you need to pass the fields in an array as the noSortedEdges argument

Related

How to validate with .when statement in yup with parent object

I have this yup which is works:
const exceptionSchema = yup.object().shape({
exceptionRule: yup.object().shape({
exceptionCondition: yup.object().shape({
exceptionCount: yup.string().required('Exception_Count_Required')
}),
selectedExceptions: yup
.array()
.ensure()
.min(1, 'Validate_ExceptionsAssigned')
})
});
But I have to validate this subobject selectedExceptions when some other property is equals to 1:
and this doesnt work:
const exceptionSchema = yup.object().shape({
exceptionRule: yup.object().shape({
exceptionCondition: yup.object().shape({
exceptionCount: yup.string().required('Exception_Count_Required')
}),
selectedExceptions: yup
.array()
.ensure()
// .min(1, 'Validate_ExceptionsAssigned')
.when('type', {
is: 1,
then: yup
.array()
.ensure()
.min(1, 'Validate_ExceptionsAssigned')
})
})
});
this is mine model, and I think because the type proeprty is not within this object exceptionRule then yup validation does not take into consideration this .when() clause.
Model:
const newRule = () => ({
type: '',
exceptionRule: {
exceptionCondition: {
exceptionCount: '1',
condition: 1,
frequency: 1
},
selectedExceptions: [],
}
});
How to get this type from above, outside my inner object?
How can I debug this when('type') to be sure what value does it have?

How to make custom error message using Joi?

How to make a custom message using joi? i saw many answered question related on this but i dont know why it didnt work on my end, the error message always appeared is "Student" does not contain 1 required value(s) what i want is "Student" This field is required.
export const VALIDATION_SCHEMA = {
students: Joi.array()
.label('Student Name(s)')
.items(
Joi.object({
name: Joi.string(),
value: Joi.string()
}).required().messages('"Student" This field is required.')
),
}
You can return a custom error object using Error constructor like this:
var schema = Joi.object().keys({
firstName: Joi.string().min(4).max(8).required().error(new
Error('error message here for first name')),
lastName: Joi.string().min(5).max(1).required().error(new
Error('error message here for last name'))
});
Joi.validate(req.body, schema, function(err, value) {
if (err) {
console.log(err.message)
return catched(err.message);
}
});
The easiest way in my opinion would be this.
const Joi = require("#hapi/joi");
export const categorySchema = Joi.object({
mobile: Joi.string().trim().regex(/^[6-9]\d{9}$/).required().messages({
"string.base": `"" should be a type of string`,
"string.empty": `"" must contain value`,
"string.pattern.base": `"" must be 10 digit number`,
"any.required": `"" is a required field`
}),
password: Joi.string().trim().required().messages({
"string.base": `"" should be a type of 'text'`,
"string.pattern.base": `"" must be 10 digit number`,
"any.required": `"" is a required field`
}),
}).required();

yup conditional validation of array

I want to validate the array on the base of loan register if loan register is true then array should validate else not.
yup.object().shape({
loan_register: yup.boolean(),
loans: yup.array()
.of(
yup.object().shape({
bank_name: yup.string().required(),
bank_reg_no: yup.string().required(),
loan_amount: yup.string().required(),
})
)
})
The reason why of is not an exported member from yup is because yup needs to know the type of data first. You can only use of once you know the type.
For example: array().of(), string().oneOf(), e.t.c
Hence, in your case, you need to provide the data type and it will solve your problem.
const validationSchema = yup.object({
loan_register: yup.boolean(),
loans: yup.array().when('loan_register', {
is: true,
then: yup.array().of(
yup.object({
bank_name: yup.string().required(),
bank_reg_no:yup.string().required(),
loan_amount:yup.string().required(),
}))
})
})
EDIT: 'When loan_register === true, bank_name, bank_reg_no and loan_amount must be strings and required fields.'
You can translate that requirement into code like following (include Yup conditional validation using .when() ):
const validationSchema = yup.object().shape({
loan_register: yup.boolean(),
loans: yup.array()
.when('loan_register', {
is: true,
then: yup.of(
yup.object().shape({
bank_name: yup.string().required(),
bank_reg_no: yup.string().required(),
loan_amount: yup.string().required(),
})
)
})
})
I think you'll want to utilize .when(). This allows exactly what you're looking for by providing conditional validation checks based off other attribute values.
It has a more explicit approach where you'd add
.when('loan_register', {is: true, then: /* yup schema */ })
I believe the change would look like
yup.object().shape({
loan_register: yup.boolean(),
loans: yup.array()
.when('loan_register', {
is: true,
then: yup.of(
yup.object().shape({
bank_name: yup.string().required(),
bank_reg_no: yup.string().required(),
loan_amount: yup.string().required(),
})
)
})
})

Yup: Ensure at least one of the elements in an array is valid

I have the following schema:
const baseSchema = {
name: Yup.array().of(
Yup.object().shape({
lang: Yup.string().required(),
value: Yup.string()
.min(2)
.max(20)
.required()
})
)
};
Is there a way to consider the schema valid if at least one of the objects in name is valid?
Edit: essentially it has to use when(), but what I'm struggling with is to find out how to check the other elements in the array.
You can use xor if you want one of them to be required:
const baseSchema = {
name: Yup.array().of(
Yup.object().shape({
lang: Yup.string().required().allow(""),
value: Yup.string()
.min(2)
.max(20)
.required().allow("")
})
)
};

Conditional validation with Yup and Formik

Here is my validation schema:
const validationSchema = Yup.object().shape({
person: Yup.object().shape({
name: Yup.string().required('Field is required'),
surname: Yup.string().required('Field is required'),
middleName: Yup.string().required('Field is required'),
email: Yup.string()
.email('Wrong e-mail format')
.required('Field is required')
}),
company: Yup.object().shape({
name: Yup.string().required('Field is required'),
address: Yup.string().required('Field is required'),
email: Yup.string()
.email('Wrong e-mail format')
.required('Field is required')
})
});
And also there are two variables in React State: isPerson and isCompany. How to make validation work conditionally, for example if isPerson is true, then person in validationSchema is required to be validated?
Updated ans: 2020.
you can use Yup conditions
const validationSchema = Yup.object().shape({
isCompany: Yup.boolean(),
companyName: Yup.string().when('isCompany', {
is: true,
then: Yup.string().required('Field is required')
}),
companyAddress: Yup.string().when('isCompany', {
is: (isCompany) => true,//just an e.g. you can return a function
then: Yup.string().required('Field is required'),
otherwise: Yup.string()
}),
});
And make sure to update your form accordingly. I hope you get the point...
You can conditionally add to your validation schema just like any other object:
let validationShape = {
company: Yup.object().shape({
name: Yup.string().required('Field is required'),
address: Yup.string().required('Field is required'),
email: Yup.string()
.email('Wrong e-mail format')
.required('Field is required')
})
};
if (this.state.isPerson) {
validationShape.person = Yup.object().shape({
name: Yup.string().required('Field is required'),
surname: Yup.string().required('Field is required'),
middleName: Yup.string().required('Field is required'),
email: Yup.string()
.email('Wrong e-mail format')
.required('Field is required');
}
const validationSchema = Yup.object().shape(validationShape);
Conditional validations with YUP :
All kinds of validation that can be done with when are as follows:
1. Single value, simple condition :
RULE: Only ask for personName when isPerson true.
personName : Yup.string().when('isPerson', {
is: true,
then: Yup.string().required('Field is required'),
otherwise: Yup.string(),
})
2. Single value, complex condition :
RULE: Only ask for personName when company is "IT".
personName : Yup.string().when('company', {
is: (company)=> company==='IT',
then: Yup.string().required('Field is required'),
otherwise: Yup.string(),
})
3. Multi value, complex condition :
RULE: Only ask for personName when company is "IT" and person is valid.
personName : Yup.string().when(['company', 'isPerson'], {
is: (company,isPerson)=> company==='IT'&& isPerson,
then: Yup.string().required('Field is required'),
otherwise: Yup.string(),
})
While the accepted solution works, it had one problem - two of the fields to be validated were common, and had to be duplicated. In my case, I had majority of the fields common with just 2-4 outliers.
So here is another solution:
Define each schema separately - i.e. 3 schemas - commonSchema for the common fields, personSchema for person specfic fields & companySchema for company specific fields.
Merge the schemas based on the state
const validationSchema = isPerson
? commonSchema.concat(personSchema)
: commonSchema.concat(companySchema)
For details on "concat", refer to the yup docs on github.
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.

Categories

Resources