Skip to content

Commit

Permalink
Fix #267
Browse files Browse the repository at this point in the history
  • Loading branch information
Semigradsky committed Dec 2, 2015
1 parent fbd0911 commit 7481b6d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 56 deletions.
113 changes: 57 additions & 56 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,17 @@ Formsy.Form = React.createClass({
detachFromForm: this.detachFromForm,
validate: this.validate,
isFormDisabled: this.isFormDisabled,
isValidValue: function (component, value) {
isValidValue: (component, value) => {
return this.runValidation(component, value).isValid;
}.bind(this)
}
}
}
},

// Add a map to store the inputs of the form, a model to store
// the values of the form and register child inputs
componentWillMount: function () {
this.inputs = {};
this.model = {};
this.inputs = [];
},

componentDidMount: function () {
Expand Down Expand Up @@ -110,62 +109,65 @@ Formsy.Form = React.createClass({
// If any inputs have not been touched yet this will make them dirty
// so validation becomes visible (if based on isPristine)
this.setFormPristine(false);
this.updateModel();
var model = this.mapModel();
var model = this.updateModel();
model = this.mapModel(model);
this.props.onSubmit(model, this.resetModel, this.updateInputsWithError);
this.state.isValid ? this.props.onValidSubmit(model, this.resetModel, this.updateInputsWithError) : this.props.onInvalidSubmit(model, this.resetModel, this.updateInputsWithError);

},

mapModel: function () {
mapModel: function (model) {

if (this.props.mapping) {
return this.props.mapping(this.model)
return this.props.mapping(model)
} else {
return formDataToObject(Object.keys(this.model).reduce(function (mappedModel, key) {
return formDataToObject(Object.keys(model).reduce((mappedModel, key) => {

var keyArray = key.split('.');
var base = mappedModel;
while (keyArray.length) {
var currentKey = keyArray.shift();
base = (base[currentKey] = keyArray.length ? base[currentKey] || {} : this.model[key]);
base = (base[currentKey] = keyArray.length ? base[currentKey] || {} : model[key]);
}

return mappedModel;

}.bind(this), {}));
}, {}));
}
},

// Goes through all registered components and
// updates the model values
updateModel: function () {
Object.keys(this.inputs).forEach(function (name) {
var component = this.inputs[name];
this.model[name] = component.state._value;
}.bind(this));
return this.inputs.reduce((model, component) => {
var name = component.props.name;
model[name] = component.state._value;
return model;
}, {});
},

// Reset each key in the model to the original / initial / specified value
resetModel: function (data) {
Object.keys(this.inputs).forEach(function (name) {
this.inputs.forEach(component => {
var name = component.props.name;
if (data && data[name]) {
this.inputs[name].setValue(data[name]);
component.setValue(data[name]);
} else {
this.inputs[name].resetValue();
component.resetValue();
}
}.bind(this));
});
this.validateForm();
},

setInputValidationErrors: function (errors) {
Object.keys(this.inputs).forEach(function (name, index) {
var component = this.inputs[name];
this.inputs.forEach(component => {
var name = component.props.name;
var args = [{
_isValid: !(name in errors),
_validationError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]
}];
component.setState.apply(component, args);
}.bind(this));
});
},

// Checks if the values have changed from their initial value
Expand All @@ -174,9 +176,8 @@ Formsy.Form = React.createClass({
},

getPristineValues: function() {
var inputs = this.inputs;
return Object.keys(inputs).reduce(function (data, name) {
var component = inputs[name];
return this.inputs.reduce((data, component) => {
var name = component.props.name;
data[name] = component.props.value;
return data;
}, {});
Expand All @@ -186,49 +187,45 @@ Formsy.Form = React.createClass({
// stored in the inputs map. Change their state to invalid
// and set the serverError message
updateInputsWithError: function (errors) {
Object.keys(errors).forEach(function (name, index) {
var component = this.inputs[name];

Object.keys(errors).forEach((name, index) => {
var component = utils.find(this.inputs, component => component.props.name === name);
if (!component) {
throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));
throw new Error('You are trying to update an input that does not exist. ' +
'Verify errors object with input names. ' + JSON.stringify(errors));
}
var args = [{
_isValid: this.props.preventExternalInvalidation || false,
_externalError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]
}];
component.setState.apply(component, args);
}.bind(this));
});
},

isFormDisabled: function () {
return this.props.disabled;
},

getCurrentValues: function () {
return Object.keys(this.inputs).reduce(function (data, name) {
var component = this.inputs[name];
return this.inputs.reduce((data, component) => {
var name = component.props.name;
data[name] = component.state._value;
return data;
}.bind(this), {});
}, {});
},

setFormPristine: function (isPristine) {
var inputs = this.inputs;
var inputKeys = Object.keys(inputs);

this.setState({
_formSubmitted: !isPristine
})
_formSubmitted: !isPristine
});

// Iterate through each component and set it as pristine
// or "dirty".
inputKeys.forEach(function (name, index) {
var component = inputs[name];
this.inputs.forEach((component, index) => {
component.setState({
_formSubmitted: !isPristine,
_isPristine: isPristine
});
}.bind(this));
});
},

// Use the binded values and the actual input value to
Expand Down Expand Up @@ -362,16 +359,13 @@ Formsy.Form = React.createClass({
// Validate the form by going through all child input components
// and check their state
validateForm: function () {
var allIsValid;
var inputs = this.inputs;
var inputKeys = Object.keys(inputs);

// We need a callback as we are validating all inputs again. This will
// run when the last component has set its state
var onValidationComplete = function () {
allIsValid = inputKeys.every(function (name) {
return inputs[name].state._isValid;
}.bind(this));
var allIsValid = this.inputs.every(component => {
return component.state._isValid;
});

this.setState({
isValid: allIsValid
Expand All @@ -392,8 +386,7 @@ Formsy.Form = React.createClass({

// Run validation again in case affected by other inputs. The
// last component validated will run the onValidationComplete callback
inputKeys.forEach(function (name, index) {
var component = inputs[name];
this.inputs.forEach((component, index) => {
var validation = this.runValidation(component);
if (validation.isValid && component.state._externalError) {
validation.isValid = false;
Expand All @@ -403,12 +396,12 @@ Formsy.Form = React.createClass({
_isRequired: validation.isRequired,
_validationError: validation.error,
_externalError: !validation.isValid && component.state._externalError ? component.state._externalError : null
}, index === inputKeys.length - 1 ? onValidationComplete : null);
}.bind(this));
}, index === this.inputs.length - 1 ? onValidationComplete : null);
});

// If there are no inputs, set state where form is ready to trigger
// change event. New inputs might be added later
if (!inputKeys.length && this.isMounted()) {
if (!this.inputs.length && this.isMounted()) {
this.setState({
canChange: true
});
Expand All @@ -418,16 +411,24 @@ Formsy.Form = React.createClass({
// Method put on each input component to register
// itself to the form
attachToForm: function (component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state._value;

if (this.inputs.indexOf(component) === -1) {
this.inputs.push(component);
}

this.validate(component);
},

// Method put on each input component to unregister
// itself from the form
detachFromForm: function (component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
var componentPos = this.inputs.indexOf(component);

if (componentPos !== -1) {
this.inputs = this.inputs.slice(0, componentPos)
.concat(this.inputs.slice(componentPos + 1));
}

this.validateForm();
},
render: function () {
Expand Down
10 changes: 10 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,15 @@ module.exports = {
}

return a === b;
},

find: function (collection, fn) {
for (var i = 0, l = collection.length; i < l; i++) {
var item = collection[i];
if (fn(item)) {
return item;
}
}
return null;
}
};

0 comments on commit 7481b6d

Please sign in to comment.