Michael Edwards
Michael Edwards

Reputation: 6528

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?

Upvotes: 39

Views: 65961

Answers (4)

Judith lobo
Judith lobo

Reputation: 267

    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')

   }),

Upvotes: 0

Ozal  Zarbaliyev
Ozal Zarbaliyev

Reputation: 638

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

Upvotes: 4

Daksh Lohar
Daksh Lohar

Reputation: 381

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),
    }),

Upvotes: 22

tatsuya kanemoto
tatsuya kanemoto

Reputation: 2028

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.

Upvotes: 49

Related Questions