brutusin / json-forms

JSON Schema to HTML form generator, supporting dynamic subschemas (on the fly resolution). Extensible and customizable library with zero dependencies. Bootstrap add-ons provided
http://brutusin.org/json-forms
Apache License 2.0
607 stars 168 forks source link

Dependencies #60

Closed vincentmorneau closed 6 years ago

vincentmorneau commented 7 years ago

Would dependencies be complicated to implement?

https://spacetelescope.github.io/understanding-json-schema/reference/object.html#property-dependencies

I believe all scenarios of required fields would be covered.

If it's too much work I'll work around it.

idelvall commented 7 years ago

It can be done, give it a try Vincent. One recommendation: Use https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.19 as reference

The linked example by you seems to not to adhere to the spec:

If the dependency value is an object, it MUST be a valid JSON Schema. If the dependency key is a property in the instance, the dependency value must validate against the entire instance.

vincentmorneau commented 7 years ago

I realize there are very little examples out there and it's probably a lesser known feature of JSON Schema.

I tried https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343 but no luck.

I'll close this issue and I'll manage without it. Thanks!

vincentmorneau commented 7 years ago

I just learned about the oneOf functionnality, but haven't been able to use it properly.

In the following example, I need jsConcat.finalName to be required ONLY if jsConcat.enabled is true.

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "title": "Root",
    "properties": {
        "appURL": {
            "type": "string",
            "title": "Application URL"
        },
        "jsConcat": {
            "type": "object",
            "title": "JavaScript Concatenation",
            "properties": {
                "enabled": {
                    "type": "boolean",
                    "title": "Enabled"
                },
                "finalName": {
                    "type": "string",
                    "title": "Concatenated Name",
                }
            },
            "required": [
                "enabled"
            ]
        }
    },
    "required": [
        "appURL"
    ]
}

Again, if it's too much work let me know I'll try to find an alternative.

idelvall commented 7 years ago

Hi, Vicent despite not being a standard feature, you can use dynamic schemas for this case:

schema:

{
    "type": "object",
    "title": "Root",
    "properties": {
        "appURL": {
            "type": "string",
            "title": "Application URL"
        },
        "jsConcat": {
            "type": "object",
            "title": "JavaScript Concatenation",
            "properties": {
                "enabled": {
                    "type": "boolean",
                    "title": "Enabled"
                },
                "finalName": {
                      "dependsOn":["enabled"]
                }
            },
            "required": [
                "enabled"
            ]
        }
    },
    "required": [
        "appURL"
    ]
}

schema resolver:

function (names, data, cb) {
    var schemas = new Object();
    if(data && data.jsConcat && data.jsConcat.enabled===true){
        schemas["$.jsConcat.finalName"] = {"type":"string","title": "Concatenated Name"}
    } else {
        schemas["$.jsConcat.finalName"] = {}
    }
    cb(schemas);
}

This gives you more flexibility, given that the dependencies are dynamic, ie, can change depending on the actual data. Hopefully this fits your needs ...

vincentmorneau commented 7 years ago

That will do it for now.

I may look into automating the schema resolver. I'm sure it can all be done right from the original JSON Schema using the dependencies keyword.

Thanks again.

mfulton26 commented 7 years ago

I would like to be able to conditionally display questions declaratively and I believe this could be supported through dependencies as well. Does the following example make sense?

{
  "title": "Person",
  "type": "object",
  "properties": {
    "Do you have any pets?": {
      "type": "string",
      "enum": [
        "",
        "No",
        "Yes: One",
        "Yes: More than one"
      ]
    },
    "How old is your pet?": {
      "type": "number"
    },
    "How old is your oldest pet?": {
      "type": "number"
    }
  },
  "required": [
    "Do you have any pets?"
  ],
  "dependencies": {
    "Do you have any pets?": {
      "oneOf": [
        {
          "properties": {
            "Do you have any pets?": {
              "enum": [
                "",
                "No"
              ]
            },
            "How old is your pet?": {
              "not": {}
            },
            "How old is your oldest pet?": {
              "not": {}
            }
          }
        },
        {
          "properties": {
            "Do you have any pets?": {
              "enum": [
                "Yes: One"
              ]
            },
            "How old is your oldest pet?": {
              "not": {}
            }
          },
          "required": [
            "How old is your pet?"
          ]
        },
        {
          "properties": {
            "Do you have any pets?": {
              "enum": [
                "Yes: More than one"
              ]
            },
            "How old is your pet?": {
              "not": {}
            }
          },
          "required": [
            "How old is your oldest pet?"
          ]
        }
      ]
    }
  }
}

If "Do you have any pets?" is "" or "No" then no further questions are present nor required; if it is "Yes: One" then "How old is your pet?" is present and required but "How old is your oldest pet?" is not; if it is "Yes: More than one" then "How old is your oldest pet?" is present and required but "How old is your pet?" is not.

This would support dynamic schemas without the need for a schema resolver which would simplify things, especially when array objects need to be dynamic, etc.