Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ export default class Form extends Component {
const liveValidate = props.liveValidate || this.props.liveValidate;
const mustValidate = edit && !props.noValidate && liveValidate;
const { definitions } = schema;
const formData = getDefaultFormState(schema, props.formData, definitions);
const formData = getDefaultFormState(
schema,
props.formData,
definitions,
true
);
const { errors, errorSchema } = mustValidate
? this.validate(formData, schema)
: {
Expand Down
32 changes: 26 additions & 6 deletions src/components/fields/ArrayField.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,26 +202,45 @@ class ArrayField extends Component {

onAddClick = event => {
event.preventDefault();
const { schema, registry, formData } = this.props;
const { schema, registry, required } = this.props;
const { definitions } = registry;
let formData = this.props.formData;
let itemSchema = schema.items;
if (isFixedItems(schema) && allowAdditionalItems(schema)) {
itemSchema = schema.additionalItems;
}

this.props.onChange(
[...formData, getDefaultFormState(itemSchema, undefined, definitions)],
[
...formData,
getDefaultFormState(itemSchema, undefined, definitions, required),
],
{ validate: false }
);
};

onDropIndexClick = index => {
console.log("DROP INDEX");
return event => {
if (event) {
event.preventDefault();
}
const { formData, onChange } = this.props;
const { onChange, required } = this.props;
let formData = this.props.formData;

// if field isn't required and doesn't contain any entries
// remove the whole property from formData to guarantee correct validation
if (!required) {
formData = formData.filter(item => {
return item !== null && item !== undefined;
});

formData = formData.length ? formData : undefined;
}
// refs #195: revalidate to ensure properly reindexing errors
onChange(formData.filter((_, i) => i !== index), { validate: true });

formData = formData ? formData.filter((_, i) => i !== index) : formData;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be doing this filter before the if (!required) check?

onChange(formData, { validate: true });
};
};

Expand Down Expand Up @@ -250,12 +269,13 @@ class ArrayField extends Component {
onChangeForIndex = index => {
return value => {
const { formData, onChange } = this.props;
const newFormData = formData.map((item, i) => {
let newFormData = formData.map((item, i) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Does this still need to be let?

// We need to treat undefined items as nulls to have validation.
// See https://github.com/tdegrunt/jsonschema/issues/206
const jsonValue = typeof value === "undefined" ? null : value;
return index === i ? jsonValue : item;
});

onChange(newFormData, { validate: false });
};
};
Expand Down Expand Up @@ -576,7 +596,7 @@ if (process.env.NODE_ENV !== "production") {
errorSchema: PropTypes.object,
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func,
formData: PropTypes.array,
//formData: PropTypes.array,
required: PropTypes.bool,
disabled: PropTypes.bool,
readonly: PropTypes.bool,
Expand Down
27 changes: 26 additions & 1 deletion src/components/fields/ObjectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,36 @@ class ObjectField extends Component {

onPropertyChange = name => {
return (value, options) => {
const newFormData = { ...this.props.formData, [name]: value };
const newFormData = this.getCleanFormDataFromValues({
...this.props.formData,
[name]: value,
});
this.props.onChange(newFormData, options);
};
};

getCleanFormDataFromValues = values => {
const { required } = this.props;

if (required) {
return values;
}

const cleanValues = [];

for (let key in values) {
if (values.hasOwnProperty(key) && typeof values[key] !== "undefined") {
cleanValues.push(values[key]);
}
}

if (!cleanValues.length) {
values = undefined;
}

return values;
};

render() {
const {
uiSchema,
Expand Down
30 changes: 26 additions & 4 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ export function getWidget(schema, widget, registeredWidgets = {}) {
throw new Error(`No widget "${widget}" for type "${type}"`);
}

function computeDefaults(schema, parentDefaults, definitions = {}) {
function computeDefaults(
schema,
parentDefaults,
definitions = {},
required = false
) {
// Compute the defaults recursively: give highest priority to deepest nodes.
let defaults = parentDefaults;
if (isObject(defaults) && isObject(schema.default)) {
Expand All @@ -126,6 +131,10 @@ function computeDefaults(schema, parentDefaults, definitions = {}) {
defaults = schema.default;
}

if (!required) {
return defaults;
}

switch (schema.type) {
// We need to recur for object schema inner default values.
case "object":
Expand All @@ -136,7 +145,8 @@ function computeDefaults(schema, parentDefaults, definitions = {}) {
acc[key] = computeDefaults(
schema.properties[key],
(defaults || {})[key],
definitions
definitions,
schema.required && schema.required.indexOf(key) > -1
);
return acc;
},
Expand All @@ -153,12 +163,22 @@ function computeDefaults(schema, parentDefaults, definitions = {}) {
return defaults;
}

export function getDefaultFormState(_schema, formData, definitions = {}) {
export function getDefaultFormState(
_schema,
formData,
definitions = {},
required = false
) {
if (!isObject(_schema)) {
throw new Error("Invalid schema: " + _schema);
}
const schema = retrieveSchema(_schema, definitions);
const defaults = computeDefaults(schema, _schema.default, definitions);
const defaults = computeDefaults(
schema,
_schema.default,
definitions,
required
);
if (typeof formData === "undefined") {
// No form data? Use schema defaults.
return defaults;
Expand Down Expand Up @@ -197,6 +217,8 @@ export function isObject(thing) {

export function mergeObjects(obj1, obj2, concatArrays = false) {
// Recursively merge deeply nested objects.
obj1 = obj1 || {};
obj2 = obj2 || {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this change about?

var acc = Object.assign({}, obj1); // Prevent mutation of source object.
return Object.keys(obj2).reduce(
(acc, key) => {
Expand Down