How to validate interdependent strings using yup in react native - javascript

I have two strings of date start_date and end_date. Below have empty string as there initial value:
export interface FilterSchema {
start_date?: any;
end_date?: any;
}
const initialValues: FilterSchema = {
start_date: '',
end_date: '',
};
Initially they both are empty. But if one of them is selected, then other also needs to be selected. If none of them is selected then there is no need of validation. So I used yup for this in following way:
const filterSchema = yup.object().shape({
start_date: yup.string().when('end_date', {
is: value => value && value != '',
then: yup.string().required('Required'),
}),
end_date: yup.string().when('start_date', {
is: value => value && value != '',
then: yup.string().required('Required'),
}),
});
But I am getting error message:
Cyclic dependency, node was: "end_date"
I am using filterSchema in Formik:
<Formik
onSubmit={facilityFilter}
validationSchema={filterSchema}
....

Fields that depend on another field need to be constructed in the right order, i.e. If endDate depends on startDate then startDate needs to go first, however in your case you want to validate after everything is constructed. Yup isn't able to figure this out so it throws the cyclic dependency error.
If you want to tell Yup to ignore the ordering you can add an array of field pairs to the second argument in the shape method
const filterSchema = yup.object().shape({
start_date: yup.string().when('end_date', {
is: value => value && value != '',
then: yup.string().required('Required'),
}),
end_date: yup.string().when('start_date', {
is: value => value && value != '',
then: yup.string().required('Required'),
}),
}, [["start_date", "end_date"]]);
It's not the best solution for dealing with cyclical dependencies as noted here by the creator of yup: https://github.com/jquense/yup/issues/720

Related

Yup How to get the value of field using .when on the same field?

How to get the value of field when using when on the same field ?
email: yup
.string()
.when([], {
is: (val: string) => {
console.log('val = ', val)
return true
},
then: (schema) => {
console.log('schema = ', schema)
return schema
},
})
})
I am assuming you want to get the value of email in when condition at is location as well as at then location. then you have wrote everything correctly. just remove starting [] empty array in when condition. It you don't provide any array or string then value of current item will get at is location.
ref: React-hook-form validation when input is empty. Conflict with Yup.matches
I have updated the below as per your latest comment. I have tested this in my JavaScript file. so you might need to convert it as per TSX syntax.
email: yup
.string()
.when({
is: (val) => {
console.log('val = ', val)
return true
},
then: yup.string()
.test('testKey', 'error', (value) => console.log('value at then', value)),
})
})

Why yup is trigger my tests even though previous tests are failing?

I want to validate my object with the schema using yup.
But I notice that when I wrote my own test function it's trigger anyway.
I mean I validate the age property for number, null, positive, integer value. then I want to continue with my own logic test.
So I expect to NOT enter the function unless the previous tests are valid.
I'm not sure if this is how it meant to be, but in this way I must also check for valid input in my tests function, even though I add the number, null, positive, integer checks.
So am I using the yup wrong?
What I expect form yup is not invoke the test if the previous tests are invalid.
stackblitz
import { object, string, number, date, InferType } from 'yup';
let userSchema = object({
age: number()
.nullable()
.positive()
.integer()
.test({
message: 'test message',
test: (v) => {
console.log('in test!', v);
return !!v.toPrecision();
},
}),
});
userSchema
.validate({ age: null })
.then((res) => {
console.log({ res });
})
.catch((e) => {
console.log({ e });
});

Mongoose automatically change the type of the value

In mongoose.model, I have chosen the type of name to be a string and the type of age to be a number, but when I enter a number as the value of name, I don't get an error and the same thing happens when I use something like '18' as the value of age.
Here is the code:
const User = mongoose.model('User', {
name: { type: String },
age: { type: Number }
});
const me = new User({
name: 12,
age: '18'
});
me.save().then(() => console.log(me)).catch(error => console.log(error));
Mongoose casts the values to the corresponding type, if it fails a CastError is thrown, from the doc:
Before running validators, Mongoose attempts to coerce values to the
correct type. This process is called casting the document. If casting
fails for a given path, the error.errors object will contain a CastError object.
You can try this by given age the value 'aa' for example.
If you want to override this behavior you can use one of the following options:
Disable casting globally: mongoose.Number.cast(false)
Disable casting just for a given path:
age: {
type: Number,
cast: false // Disable casting just for this path
},
Use a custom function:
age: {
type: Number,
cast: v => { return typeof v === 'number' && !isNaN(v) ? Number(v) : v; } // Override casting just for this path
}

Yup.string.when will always give the result undefined. How can i pass a string value into the .when method

below my code.
For some reason, the iscountrycode is undefined when I put it into the .when.
in the console.log the result is 'BE'
export function getCheckoutFormSchema(t?: any, iscountryCode?: string) {`
console.log(iscountryCode)`
let schema = Yup.object().shape({`
zip: Yup.string()
.when(iscountryCode, {
is: 'BE',
then: Yup.string()`
Does anybody know why this is happening?
I think you forgot to apply quotes ('') around iscountryCode field.
Try this -
let schema = Yup.object().shape({
zip: Yup.string().when('iscountryCode', { is: 'BE', then: Yup.string()...}
)
also one more point you need to fact check -
your schema must have this field you are using 'iscountryCode',
let schema = Yup.object().shape({
iscountryCode: Yup.string(),
zip: Yup.string().when('iscountryCode', { is: 'BE', then: Yup.string()...}
)
both fields should be at the sibling level.

Get the value of another field for validation in Yup Schema

I am using Formik with Yup for validation and TypeScript
I have a field that needs to validate based on the value of another field.
The first field is called price and the second field is called tips. The max tip value is 10% of the what ever the price entered is.
I tried to create validation for this using the following:
tips: yup.number()
.min(0, `Minimum tip is $0`)
.max( parseFloat(yup.ref('price'))* 0.1, "Maximum tip is 10% of the price.");
however this doesn't compile because yup.ref returns a Ref. How can I get the value of the price field in this validation?
number.max cannot reference other field and calculate with it at validation.
If you want to do this, you need to implement own schema with mixed.test.
Here is a example.
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: function (value) {
// You can access the price field with `this.parent`.
return value <= parseFloat(this.parent.price * 0.1)
},
}),
Here is doc.
You can check and try how it works here.
I hope this will help you.
if you don't want to use this.parent to access the other properties values you can use the context.
tips: number()
.min(0, `Minimum tip is $0`)
.test(
'max',
'${path} must be less than 10% of the price',
(value, context) => value <= parseFloat(context.parent.price * 0.1),
),
// OR
tips: number()
.min(0, `Minimum tip is $0`)
.test({
name: 'max',
exclusive: false,
params: { },
message: '${path} must be less than 10% of the price',
test: (value, context) => value <= parseFloat(context.parent.price * 0.1),
}),
For referencing other field value we can use this.parent or ctx.parent in case if our value is not nested.
object({
title: string(),
price: string().test('test-name', 'test-message', (value, ctx) => {
let title = ctx.parent.title;
}),
foo: string()
.test('test-name1', 'test-message1', function (value) {
let title = this.parent.title
})
})
but if we have nested value parent is going to give parent of nested
value. in this case parent is not going to work if we want to access
very parent value. we can access parent value with ctx.from. ctx.from contains parents from bottom to top. for example:
object({
title: string(),
ourObject: object({
param1: string(),
param2: string()
.test('test-name', 'test-msg', (value, ctx) => {
let title = ctx.from[ctx.from.length - 1].value.title
})
})
})
or we can easily access any data we want with providing context to schema when validating
object({
price: string(),
foo: string()
.test('test-name', 'test-message', (value, ctx) => {
let arr = ctx.context.arr;
})
})
.validate({ price: 5, foo: 'boo' }, { context: { arr: [1, 2] } })
.then(() => ...)
.catch((err) => ...)
doc
LIKE SO
.test((val,obj)=>{
console.log(val,obj.parent.otherField)
})
val=>current field value
obj=>object of the whole schema
EXAMPLE::::
date: yup.
string()
.required()
.test('type', 'Date should be future date', (val) => {
console.log(val);
}),
time: yup
.string()
.required()
.test('type', 'Time should be future time', (val,obj) => {
console.log(val,'in time val',obj.parent.date,'date')
}),

Categories

Resources