Search code examples
reactjsjsonschemareact-jsonschema-forms

How to create a typical country/province (or state) dependency dropdowns with react-jsonschema-forms?


I'm trying to code an example that shows two dropdowns: one for countries, and one for related states/provinces. When you choose a country on the first dropdown, the values of the second dropdown should be updated to display only the provinces/states of the selected country. Usually this is accomplished using some sort of Ajax call, but in this case the requirement is to try it purely with react-jsonschema-form. Is this actually possible ? I have seen several examples using dependencies / references, but none solving this particular scenario.

So far I came up with this: https://codesandbox.io/s/fervent-cherry-lokjk?file=/src/App.js

{
  "definitions": {
    "Countries": {
      "title": "Country",
      "type": "object",
      "properties": {
          "country": {
              "type": "string",
              "enum": [
                "CANADA",
                "USA",
                "MEXICO"
              ]              
          }
      },
      "dependencies": {
        "country": {
          "oneOf": [
           {
              "properties": {
                "country": {
                  "enum": [
                    "CANADA"
                  ]
                },
                "province": {
                  "type": "string",
                  "title": "Province",
                  "enum": [
                    "AB",
                    "BC",
                    "MB"
                  ]                  
                }
              }
            },
            {
              "properties": {
                "country": {
                  "enum": [
                    "USA"
                  ]
                },
                "province": {
                  "type": "string",
                  "title": "State",
                  "enum": [
                    "AL",
                    "AK",
                    "AR"
                  ]                  
                }
              }
            },
            {
              "properties": {
                "country": {
                  "enum": [
                    "MEXICO"
                  ]
                },
                "province": {
                  "type": "string",
                  "title": "State",
                  "enum": [
                    "AGS",
                    "BC",
                    "BCS"
                  ]                  
                }
              }
            }
          ]
        }
      }
    }
  },
  "title": "Demo for countries",
  "type": "object",
  "properties": {
      "currentCountry": {
      "$ref": "#/definitions/Countries",
      "title": "Select country / province / state"
    }
  }
}

which basically can render a Country dropdown and a State/Province dropdown. When selecting a country, the corresponding state/province dropdown is updated correctly with the new set of enum values.

However, with this approach, the form will submit a field named "currentCountry" with two properties inside, the country and the province

currentCountry: { country: "USA" , province: "AL" }

but of course the idea is to submit "country" and "province" as two different form fields, without the need to be wrapped.

So, does anyone know how to achieve this typical use case ? Is there any best approach to it using JSON schemas ?

Maybe having to code a custom widget ? (I would like to avoid that if possible)

Thanks in advance !


Solution

  • After playing a bit more, seems I found the solution. A correct schema that does exactly what is needed is this one:

    {
      title: "Demo for countries",
      type: "object",
      properties: {
        country: {
          type: "string",
          title: "Country",
          enum: ["CANADA", "USA", "MEXICO"]
        }
      },
      dependencies: {
        country: {
          oneOf: [
            {
              properties: {
                country: {
                  enum: ["CANADA"]
                },
                province: {
                  type: "string",
                  title: "Province",
                  enum: ["ALBERTA", "BRITISH COLUMBIA", "MANITOBA"]
                }
              }
            },
            {
              properties: {
                country: {
                  enum: ["USA"]
                },
                province: {
                  type: "string",
                  title: "State",
                  enum: ["ALABAMA", "OREGON", "ARKANSAS"]
                }
              }
            },
            {
              properties: {
                country: {
                  enum: ["MEXICO"]
                },
                province: {
                  type: "string",
                  title: "State",
                  enum: ["JALISCO", "BAJA CALIFORNIA", "CHIAPAS"]
                }
              }
            }
          ]
        }
      }
    }
    

    and the above codesandbox example has been updated to reflect this change. Happy to know this scenario can be achieved with JSON schemas !