json-editor / json-editor

JSON Schema Based Editor
MIT License
4.47k stars 660 forks source link

Dependency Question #644

Closed mattferderer closed 4 years ago

mattferderer commented 4 years ago

General information

I'm wondering if there is a way to use Dependencies with nested objects. I'm trying to have a nested input display if another input on a different level of the hierarchy is set to a certain value.

For example, here is a super simplified version. Hopefully the use of gender/age example questions are not offensive.

Based on the "gender" selected a question specific to that gender shows up.

If I nest some other questions in an object named "Age" that relate to the original "gender" question, the dependency feature doesn't work. I'm curious if there is a way to make it work without adding JavaScript. I've tried using IDs as mentioned with the watch feature & periods to separate levels.

{
  "title": "Person",
  "type": "object",
  "required": [
    "gender"
  ],
  "properties": {
    "gender": {
      "title": "Gender",
      "type": "string",
      "enum": [
        "female",
        "male",
        "other"
      ],
      "options": {
        "inputAttributes": {
          "id": "genderSelect"
        }
      }
    },
    "maleSpecific": {
      "type": "string",
      "title": "Male specific question?",
      "options": {
        "dependencies": {
          "gender": "male"
        }
      }
    },
    "femaleSpecific": {
      "type": "string",
      "title": "Female specific question?",
      "options": {
        "dependencies": {
          "gender": "female"
        }
      }
    },
    "otherSpecific": {
      "type": "string",
      "title": "What is your preferred gender?",
      "options": {
        "dependencies": {
          "gender": "other"
        }
      }
    },
    "age": {
      "type": "object",
      "options": {
        "compact": true
      },
      "properties": {
        "maleSpecificAge": {
          "type": "string",
          "title": "Male specific age question?",
          "options": {
            "dependencies": {
              "gender": "male"
            },
            "inputAttributes": {
              "id": "maleAge"
            }
          }
        },
        "femaleSpecificAge": {
          "type": "string",
          "title": "Female specific age question?",
          "options": {
            "dependencies": {
              "gender": "female"
            },
            "inputAttributes": {
              "id": "femaleAge"
            }
          }
        },
        "otherSpecificAge": {
          "type": "string",
          "title": "Other specific age question?",
          "options": {
            "dependencies": {
              "gender": "other"
            },
            "inputAttributes": {
              "id": "otherAge"
            }
          }
        }
      }
    }
  }
}
pmk65 commented 4 years ago

I looks like dependencies only work when the property is on the same level. But you could combine it with the watch property. On your 2nd level, create a hidden field that watches the root.gender property. Then use value of the hidden fields for your dependencies on the 2nd level.

mattferderer commented 4 years ago

Thanks @pmk65 I just figured this out! It looks like this part only goes up 1 level in the path[path.length - 1] = dependency

registerDependencies: function () {
    this.dependenciesFulfilled = true
    var deps = this.options.dependencies
    if (!deps) {
      return
    }

    var self = this
    Object.keys(deps).forEach(function (dependency) {
      var path = self.path.split('.')
      path[path.length - 1] = dependency
      path = path.join('.')
      var choices = deps[dependency]
      self.jsoneditor.watch(path, function () {
        self.checkDependency(path, choices)
      })
    })
  }

I'll dig into the hidden fields documentation. Any thoughts on a PR that would allow you to put in a path that skips the split/join logic? Such as if it starts with root or a hashtag/number/pound sign?

mattferderer commented 4 years ago

I did get the watch to work with hidden fields. In case anyone else is looking to do this I added something like the following:


    "age": {
      "type": "object",
      "options": {
        "compact": true
      },
      "properties": {
        "genderHidden": {
          "watch": {
            "g": "gender"
          },
          "template": "{{g}}",
          "options": {
            "hidden": true
          }
        },
        "maleSpecificAge": {
          "type": "string",
          "title": "Male specific age question?",
          "options": {
            "dependencies": {
              "genderHidden": "male"
            },
            "inputAttributes": {
              "id": "maleAge"
            }
          }

Thank you @pmk65

pmk65 commented 4 years ago

I'll dig into the hidden fields documentation. Any thoughts on a PR that would allow you to put in a path that skips the split/join logic? Such as if it starts with root or a hashtag/number/pound sign?

The watch property allows you to specify each level separated by a period, like "root.gender". And it looks a bit like the dependencies property "reverse" the "watch" keyword and value.

If you can come up with a PR that doesn't break the watch functionality (dependenciesuses the watch functionality), feel free to submit it.

RelicOfTesla commented 1 year ago

@mattferderer

output "genderHidden", how can I ignore this field, "hidden" not support.

{
  "gender": "male",
  "age": {
    "genderHidden": "male",
    "maleSpecificAge": "ff"
  }
}
schmunk42 commented 1 year ago

@RelicOfTesla Sorry I don't understand, please create a new issue with an example and description.

RelicOfTesla commented 1 year ago
{
  "title": "Person",
  "type": "object",
  "required": [
    "gender"
  ],
  "properties": {
    "gender": {
      "title": "Gender",
      "type": "string",
      "enum": [
        "female",
        "male",
        "other"
      ]
    },
    "maleSpecific": {
      "type": "string",
      "title": "Male specific question?",
      "options": {
        "dependencies": {
          "gender": "male"
        }
      }
    },
    "femaleSpecific": {
      "type": "string",
      "title": "Female specific question?",
      "options": {
        "dependencies": {
          "gender": "female"
        }
      }
    },
    "otherSpecific": {
      "type": "string",
      "title": "What is your preferred gender?",
      "options": {
        "dependencies": {
          "gender": "other"
        }
      }
    },
    "age": {
      "type": "object",
      "options": {
        "compact": true
      },
      "properties": {
        "genderHidden": {
          "watch": {
            "g": "gender"
          },
          "template": "{{g}}",
          "options": {
            "hidden": true
          }
        },
        "maleSpecificAge": {
          "type": "string",
          "title": "Male specific age question?",
          "options": {
            "dependencies": {
              "genderHidden": "male"
            }
          }
        },
        "femaleSpecificAge": {
          "type": "string",
          "title": "Female specific age question?",
          "options": {
            "dependencies": {
              "genderHidden": "female"
            }
          }
        },
        "otherSpecificAge": {
          "type": "string",
          "title": "Other specific age question?",
          "options": {
            "dependencies": {
              "genderHidden": "other"
            }
          }
        }
      }
    }
  }
}

step2. input form: choice "male", input maleSpecific="aaa", "maleSpecificAge"="bbb"

{
  "gender": "male",
  "maleSpecific": "aaa",
  "age": {
    "genderHidden": "male",
    "maleSpecificAge": "bbb"
  }
}

I WANT

{
  "gender": "male",
  "maleSpecific": "aaa",
  "age": {
    "maleSpecificAge": "bbb"
  }
}

@schmunk42

schmunk42 commented 1 year ago

@germanbisurgi is it possible to depend "upwards" in the current stable version?

Or is it only feasible with if-then-else in the next release...

germanbisurgi commented 1 year ago

Currently it is like this:

eduo commented 1 year ago

Currently it is like this:

  • dependencies works at the same property level or lower.
  • If-then-else has no effect on the user interface yet except for displaying validation errors.

With "Currently" you mean the commit pending to be included in the release, correct?

I was really looking for path-based dependencies but since if-then-else is not supported today I guess you're talking about what's coming.

germanbisurgi commented 1 year ago

@eduo You are correct, sorry for the confusion.

If with path-based dependencies you mean something like this:

{
  "dependencies": {
    "root.some.absolute.path": "value"
  }
}

The feature is coming soon.

eduo commented 1 year ago

Thank you. This works. I try to keep as much logic in the schema as possible, as that is easier to maintain than updates to the code.

Some stuff will probably always be unavoidable (I have a listing of thousands of districts, and you're supposed to only get the list in a popup that applies to the first two characters of your postal code. Stuff like that I will probably always handle via Ajax since loading thousands upon thousands of unnecessary records just to be able to filter them out sounds silly).