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

How to use anyOf with custom fields #3834

Closed
1 task done
burks10 opened this issue Aug 18, 2023 · 8 comments
Closed
1 task done

How to use anyOf with custom fields #3834

burks10 opened this issue Aug 18, 2023 · 8 comments

Comments

@burks10
Copy link

burks10 commented Aug 18, 2023

Prerequisites

What theme are you using?

material-ui

What is your question?

Hi all,

I have built several custom fields and am attempting to make some of them required conditionally based on if any other custom fields are not filled in. However, the form submits even when I don't fill out either of the custom fields.

My guess is that it has something to do with the default values maybe? Is it passing because empty string is an "ok" value?

Another guess is that it is because of the nested properties? Notice how my customField is named "phoneCustomField" and within that I have 2 more properties of "phone" and "phoneType", each tracking their own values. Is there a way to tell "anyOf", that I want it to check "phoneCustomField>phone"?

In example, the user must provide either a phone number OR an email in order to pass validation.

My schema looks something like this:

{
  "type": "object",
  "anyOf": [
    {
      "required": [
        "phoneCustomField"
      ]
    },
    {
      "required": [
        "emailCustomField"
      ]
    }
  ],
  "properties": {
    "phoneCustomField": {
      "properties": {
        "phone": {
          "type": "string"
        },
        "phoneType": {
          "type": "string"
        }
      }
    },
    "emailCustomField": {
      "properties": {
        "email": {
          "type": "string"
        }
        "emailType": {
          "type": "string"
        }
      }
    }
  }
}
@burks10 burks10 added needs triage Initial label given, to be assigned correct labels and assigned question labels Aug 18, 2023
@nickgros
Copy link
Contributor

@burks10 I think you're on the right track. In v5, the phoneCustomField object is created because it's a required object. The behavior can be modified with the experimental property experimental_defaultFormStateBehavior.emptyObjectFields see docs. Though I'll also note that I think default form state and any/oneOf is kind of buggy right now (#3833).

You're only requiring the object to be defined, so the empty object is considered valid.

You could add required fields like "required": ["phone", "phoneType"] to the subschemas to ensure that those fields must also be filled out to be valid.

@nickgros nickgros added awaiting response needs triage Initial label given, to be assigned correct labels and assigned and removed needs triage Initial label given, to be assigned correct labels and assigned labels Aug 25, 2023
@burks10
Copy link
Author

burks10 commented Aug 29, 2023

@nickgros thanks so much for the reply! I've made it much further since I last posted thanks to your help!

I am getting my desired functionality of "one of email or phone number must be entered", however I am getting some odd duplication and a UI artifact. Hopefully you can steer me in the right direction.

My schema now looks like this:

{
  "type": "object",
  "anyOf": [
    {
      "properties": {
        "phoneCustomField": {
          "properties": {
            "phone": {
              "minLength": 1
            }
          }
        }
      }
    },
    {
      "properties": {
        "emailCustomField": {
          "properties": {
            "email": {
              "minLength": 1
            }
          }
        }
      }
    }
  ],
  "properties": {
    "phoneCustomField": {
      "properties": {
        "phone": {
          "type": "string"
        },
        "phoneType": {
          "type": "string"
        }
      }
    },
    "emailCustomField": {
      "properties": {
        "email": {
          "type": "string"
        },
        "emailType": {
          "type": "string"
        }
      }
    }
  }
}

My UI, however is looking like this. Note that the arrows point to the "extra" UI elements that are being rendered. There is a random dropdown that contains "Option 1" and "Option 2", which seem to correlate to the number of custom fields I have in my schema. Also, at the bottom, there is yet another (duplicate) emailCustomField...

Screenshot 2023-08-29 at 3 48 51 PM

I have updated my rjsf core to 5.12.1 to see if the fix done here would help me, however it didn't: #3808

Here you can also see the duplication issue is occurring in the playground when using anyOf with customfields.
Screenshot 2023-08-29 at 4 07 06 PM

@burks10
Copy link
Author

burks10 commented Aug 30, 2023

I found a really hacky workaround to get over this issue by simply hiding the display artifacts with the following css selector. Would love a fix for this, so I will create a separate issue describing the problem in depth.

    'div.panel.panel-default.panel-body': {
        display: 'none'
    },

Screenshot 2023-08-30 at 8 18 43 AM

@nickgros
Copy link
Contributor

nickgros commented Sep 1, 2023

#3841 might fix this, we'll release 5.12.2 soon and see if your custom selector is still necessary.

@dany-nonstop
Copy link

bump. This is a pretty common feature, would like to see implemented. Thanks!

add to @burks10 's issue, I have the same issue here, it seems "required" inside anyOf/oneOf is not recognized. The minimum I can replicate this issue is similarly built on the same example.

And I've verified that it's the same across all themes. The behavior is the same in the playground. This is actually valid and all validators I tested are not complaining at all, including AJV8 or Python JSON schema validators.

{
  "type": "object",
  "anyOf": [
    {
      "required": [
        "phone"
      ]
    },
    {
      "required": [
        "email"
      ]
    }
  ],
  "properties": {
    "phone": {
      "type": "string"
    },
    "email": {
      "type": "string"
    }
  }
}

msedge_ntSokicSCd

@nickgros
Copy link
Contributor

nickgros commented Jun 28, 2024

This may be a better workaround: with ui:fieldReplacesAnyOrOneOf and "ui:field": "hidden" you can hide this undesirable anyOf field. Here's a playground link.

Ideally we would be able to detect when this field is not rendered, and not require this workaround, but hopefully this is a more elegant workaround for these cases.

@heath-freenome
Copy link
Member

@dany-nonstop @burks10 Hopefully this workaround solves your issue. If so, please close

@cgokey
Copy link

cgokey commented Jul 9, 2024

I was able to confirm this solution works perfect! Thank you!

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

No branches or pull requests

5 participants