Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Referencing a field outside the current yup object in a when function #2173

Open
kavlih opened this issue Feb 2, 2024 · 2 comments
Open

Comments

@kavlih
Copy link

kavlih commented Feb 2, 2024

I'm trying to reference a field outside the current yup object. In this example, car should only be required when name==='Pete'.
I found that the when function in Yup only has access to the current level of the schema. And because the name field is outside the additionals object, referencing name in the when function wont work.

  const schema = yup.object({
    additionals: yup.object({
      car: yup
        .string()
        .when(["name"], ([name], schema) =>
          name === "Pete"
            ? schema.required("required")
            : schema.optional().nullable()
        ),
    }),
    name: yup.string().optional().nullable(),
  });

I read that "The $ symbol is used by Yup to reference fields from the root level of the schema." so i tried the same schema but using the $, which didn't work

        .when(["$name"], ([name], schema) =>

I got it solved with putting the when function on the object level and using two seperate schemas for both cases:

    additionals: Yup.object().when('name ', ([name ], schema) => {
        return name === 'lapid'
            ? schema.shape({
                car: Yup.string().required('required'),
            })
            : schema.shape({
                car: Yup.string().optional().nullable(),
            });
    }),
    name: yup.string().optional().nullable(),

But this solution is not very pretty and brings other problems with it. If i have other fields in the additionals object, i have to declare them in both schemas, even tho they might not be a part of the condition and i get a duplicated code.
Also if i want to step through my form object (the type is based on the Yup schema) like additionals.car i get the TypeScript Error TS2339: Property car does not exist on type  {}. So this solution wont really work for me.

I tried to make a test case with the provided example. Not quite sure how it works and if i set it up correctly, but any feedback is welcomed: Codesandbox

Any help is appreciated.

@Hipnosis183
Copy link

You can use the $ symbol by passing a context object on the validate() function call.

Using your first example:

const schema = yup.object({
  additionals: yup.object({
    car: yup
      .string()
      .when(["$name"], ([name], schema) =>
        name === "Pete"
          ? schema.required("required")
          : schema.optional().nullable()
      ),
  }),
  name: yup.string().optional().nullable(),
});

schema.validate(myDataToValidate, { context: myDataToValidate });

If you have nested data you can use dot notation, e.g. $additionals.car.

@ebrannin-bw
Copy link

ebrannin-bw commented Jun 4, 2024

I don't want to change what Context is being sent for validation (Formik is handling that), so I'm working around this issue by adding a yup.test() at the end of the schema, like this:

const schema = yup.object({
  additionals: yup.object({
    car: yup.string().nullable()
  }),
  name: yup.string().optional().nullable(),
}).test({
  name: 'peteNeedsACar',
  message: 'A car is required for Pete',
  test: (value) => value.name !== 'Pete' || value.additionals.car.length !== 0,
});

Edit: This works in unit tests on the schema, but Formik seems to be ignoring it. I haven't yet figured out why.

Edit 2: My current code for this is throwing a ValidationError instead of returning true/false -- but I also had a null-vs-emptystring issue that was throwing exceptions in the UI, so I'm not sure if the exception was required.

I don't plan to check if going from ValidationError back to true/false would work, because the ValidationError is what's telling Formik which field has a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants