React-jsonschema-form: Default values in dependency are ignored

Created on 16 Nov 2017  路  4Comments  路  Source: rjsf-team/react-jsonschema-form

Prerequisites

Description

When using dependencies, the default values in the dependency object are ignored.

Steps to Reproduce

{
    "title": "Person",
    "type": "object",
    "properties": {
        "Do you have any pets?": {
            "type": "string",
            "enum": [
                "No",
                "Yes: One",
                "Yes: More than one"
            ],
            "default": "No"
        }
    },
    "required": [
        "Do you have any pets?"
    ],
    "dependencies": {
        "Do you have any pets?": {
            "oneOf": [
                {
                    "properties": {
                        "Do you have any pets?": {
                            "enum": [
                                "No"
                            ]
                        },
                        "title" : {
                            "type": "string",
                            "title": "Title",
                            "default": "A new task"
                        }
                    }
                },
                {
                    "properties": {
                        "Do you have any pets?": {
                            "enum": [
                                "Yes: One"
                            ]
                        },
                        "How old is your pet?": {
                            "type": "number"
                        }
                    },
                    "required": [
                        "How old is your pet?"
                    ]
                },
                {
                    "properties": {
                        "Do you have any pets?": {
                            "enum": [
                                "Yes: More than one"
                            ]
                        },
                        "Do you want to get rid of any?": {
                            "type": "boolean"
                        }
                    },
                    "required": [
                        "Do you want to get rid of any?"
                    ]
                }
            ]
        }
    }
}

In this case I used the given Person Example and added a Title field with a default value.
But this default value is ignored.

I figured out that the _computeDefaults_ function is never called for the elements in the dependency object.

Version

1.0.0

bug

Most helpful comment

Hi, @OmasBraten! I ran across the same issue. Do you have an update on this? I will look at what I can do to achieve the desired result.

All 4 comments

Hi, @OmasBraten! I ran across the same issue. Do you have an update on this? I will look at what I can do to achieve the desired result.

Hi @elenaHristova, since i wrote the issue i don't done any research to find a fix for this bug. I guess that it should be enough to call the _computeDefaults_ method right after the depencies are resolved. But last week i did not found the part where the dependencies are resolved.
So let me know if you achieve some progress.

Just came to this issue, it looks like the computeDefaults did not account for the dependencies.

I did some debug and made a quick fix on the complied utils.js to fix the issues.

you have first passed the formData to the computeDefault function inside getDefaultFormState

function getDefaultFormState(_schema, formData) {
  var definitions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  if (!isObject(_schema)) {
    throw new Error("Invalid schema: " + _schema);
  }
  var schema = retrieveSchema(_schema, definitions, formData);
  var defaults = computeDefaults(schema, _schema.default, definitions, formData);
  if (typeof formData === "undefined") {
    // No form data? Use schema defaults.
    return defaults;
  }
  if (isObject(formData)) {
    // Override schema defaults with form data.
    return mergeObjects(defaults, formData);
  }
  return formData || defaults;
}

and then check if it's a dependency and resolve the default


function computeDefaults(schema, parentDefaults) {
  var definitions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  var formData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};

  // Compute the defaults recursively: give highest priority to deepest nodes.
  var defaults = parentDefaults;
  if (isObject(defaults) && isObject(schema.default)) {
    // For object defaults, only override parent defaults that are defined in
    // schema.default.
    defaults = mergeObjects(defaults, schema.default);
  } else if ("default" in schema) {
    // Use schema defaults for this node.
    defaults = schema.default;
  } else if ("$ref" in schema) {
    // Use referenced schema defaults for this node.
    var refSchema = findSchemaDefinition(schema.$ref, definitions);
    return computeDefaults(refSchema, defaults, definitions, formData);
  } if (schema.hasOwnProperty("dependencies")) {
    const resolvedSchema = resolveDependencies(schema, definitions, formData);
    return computeDefaults(resolvedSchema, defaults, definitions, formData);
  } else if (isFixedItems(schema)) {
    defaults = schema.items.map(function (itemSchema) {
      return computeDefaults(itemSchema, undefined, definitions, formData);
    });
  }
  // Not defaults defined for this node, fallback to generic typed ones.
  if (typeof defaults === "undefined") {
    defaults = schema.default;
  }

  switch (schema.type) {
    // We need to recur for object schema inner default values.
    case "object":
      return Object.keys(schema.properties || {}).reduce(function (acc, key) {
        // Compute the defaults for this node, with the parent defaults we might
        // have from a previous run: defaults[key].
        acc[key] = computeDefaults(schema.properties[key], (defaults || {})[key], definitions, formData[key]);
        return acc;
      }, {});

    case "array":
      if (schema.minItems) {
        if (!isMultiSelect(schema, definitions)) {
          var defaultsLength = defaults ? defaults.length : 0;
          if (schema.minItems > defaultsLength) {
            var defaultEntries = defaults || [];
            // populate the array with the defaults
            var fillerEntries = new Array(schema.minItems - defaultsLength).fill(computeDefaults(schema.items, schema.items.defaults, definitions, formData));
            // then fill up the rest with either the item default or empty, up to minItems

            return defaultEntries.concat(fillerEntries);
          }
        } else {
          return [];
        }
      }
  }
  return defaults;
}

Would be great to get this resolved. Currently, if we do use default values in this scenario, the editor looks like it's correctly selected these - but since they don't render in the formData, it can lead to some rather serious errors if users/developers are not aware of this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anttivikman picture anttivikman  路  3Comments

mfulton26 picture mfulton26  路  3Comments

sstarrAtmeta picture sstarrAtmeta  路  3Comments

n1k0 picture n1k0  路  3Comments

pablen picture pablen  路  3Comments