Yup: validation fails if the id of the field is given as json path Ex: groups['Basic'].parameters[0].name.value

Created on 30 Jul 2018  路  7Comments  路  Source: jquense/yup

Hi

Am using formik as my form library, where am giving the id of the input element is given a json path
Ex: "groups['Basic'].parameters[0].name.value"

where form values will be generated as below:

  "values": {
    "groups": {
      "basic": {
        "parameters": [
          {
            "domain": {
              "value": "2"
            }
    } ] }

Same id is set to while creating the yup validation schema. But yup always trying get the value from form values assuming id is plain string key in form values object.

https://github.com/jquense/yup/blob/dd474ff79fb34aee5780dc0aebeac42eb6b88e48/src/object.js#L158

I think should support something like below.

if (field.validate) return field.validate(_.get(value, key), innerOptions);

Currently is there any other way or workaround to achieve the same for now?

Most helpful comment

you need to define schema's at each level like:

object().shape({
  nested: object().shape({
    arr: array().of(object().shape({ num: number().max(4) })),
  }),
});

All 7 comments

Yes, that is correct and intentional. schema shapes should match the shape of the value you are trying validate. Any other behavior would be confusing. If you want validate a deeply nested object like the above your schema should be shaped the same way:

I prepared my schema like below

        let schema = {
            "groups": {
                "Basic": {
                    "parameters": [
                        {
                            "name": {
                                "value": Yup.string().required(`Basic group parameter name is required!`)
                            }
                        }
                    ]
                },
                "Target": {
                    "parameters": [
                        {
                            "name": {
                                "value": Yup.string().required(`Target group parameter name is required!`)
                            }
                        }
                    ]
                }
            }
        };
        Yup.object().shape(schema);

Am getting the below error:

Uncaught (in promise) TypeError: field.resolve is not a function
    at http://localhost:8081/static/js/bundle.js:272687:23
    at Array.forEach (native)
    at ObjectSchema._cast (http://localhost:8081/static/js/bundle.js:272677:11)
    at ObjectSchema._validate (http://localhost:8081/static/js/bundle.js:272013:20)
    at ObjectSchema._validate (http://localhost:8081/static/js/bundle.js:272712:47)
    at ObjectSchema.validate (http://localhost:8081/static/js/bundle.js:272056:19)
    at validateYupSchema (http://localhost:8081/static/js/bundle.js:121351:54)
    at http://localhost:8081/static/js/bundle.js:121009:17
    at Formik._this.runValidationSchema (http://localhost:8081/static/js/bundle.js:121004:20)
    at Formik._this.runValidations (http://localhost:8081/static/js/bundle.js:121021:54)
    at Formik.<anonymous> (http://localhost:8081/static/js/bundle.js:121080:27)
    at callCallback (http://localhost:8081/static/js/bundle.js:199589:12)
    at commitUpdateQueue (http://localhost:8081/static/js/bundle.js:199622:7)
    at commitLifeCycles (http://localhost:8081/static/js/bundle.js:203089:11)
    at commitAllLifeCycles (http://localhost:8081/static/js/bundle.js:204167:7)
    at HTMLUnknownElement.callCallback (http://localhost:8081/static/js/bundle.js:188925:14)
    at Object.invokeGuardedCallbackDev (http://localhost:8081/static/js/bundle.js:188963:16)
    at invokeGuardedCallback (http://localhost:8081/static/js/bundle.js:189012:29)
    at commitRoot (http://localhost:8081/static/js/bundle.js:204306:7)
    at completeRoot (http://localhost:8081/static/js/bundle.js:205321:34)
    at performWorkOnRoot (http://localhost:8081/static/js/bundle.js:205265:9)
    at performWork (http://localhost:8081/static/js/bundle.js:205183:7)
    at flushInteractiveUpdates$1 (http://localhost:8081/static/js/bundle.js:205430:5)
    at batchedUpdates (http://localhost:8081/static/js/bundle.js:190968:7)
    at dispatchEvent (http://localhost:8081/static/js/bundle.js:193376:5)
    at interactiveUpdates$1 (http://localhost:8081/static/js/bundle.js:205417:12)
    at interactiveUpdates (http://localhost:8081/static/js/bundle.js:190975:10)
    at dispatchInteractiveEvent (http://localhost:8081/static/js/bundle.js:193353:3)
(anonymous) @   object.js:154
_cast   @   object.js:144
_validate   @   mixed.js:215
_validate   @   object.js:179
validate    @   mixed.js:258
validateYupSchema   @   formik.esm.js:547

Am i doing wrong? If yes, can you please provide an example schema for validating nested object.

you need to define schema's at each level like:

object().shape({
  nested: object().shape({
    arr: array().of(object().shape({ num: number().max(4) })),
  }),
});

Thanks @jquense . That works great!

More of a question:

For the same nested schema am trying to create a new addMethod for Yup.string.
Am sending reference to function as Yup.ref('groups.Basic.parameters[0].name.value')
this.resolve(ref) is undefined at the time validation even when value exists.

Is the syntax wrong? If yes, can you provide the example to use ref with nested schema!.

Any idea about above query!

Please read the documentation throughly, and the tests if needed (they demonstrate the entire api). References can only refer to sibling or child paths. and the path is relative to the place you are using it.

Just came into the issue too. This solution works fine except that we don't use lodash internally. Here's the plain JS way for future people coming into this issue (found it via Google so...)

Assuming we have such a payload where the key is dynamically created:
image

The following schema would allow us to make use of i.e. Formik nested objects support while keepin Yup to validate our inputs.

const dynamicFormSchema = Yup.lazy(obj =>
    Yup.object(
      Object.entries(obj).reduce(
        (a, [key]) => ({
          ...a,
          [key]: Yup.object()
            .shape({
              expected_value: Yup.string().required('Required'),
              operator: Yup.string().required('Required'),
              return_block_name: Yup.string()
                .required('Required')
                .min(3, 'Minimum 3 characters')
                .max(20, 'Maximum 20 characters')
            })
            .required()
        }),
        {}
      )
    )
  )
Was this page helpful?
0 / 5 - 0 ratings