diff --git a/src/main.js b/src/main.js index 60b7bef..d081a5e 100644 --- a/src/main.js +++ b/src/main.js @@ -56,9 +56,9 @@ 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) + } } } }, @@ -66,8 +66,7 @@ Formsy.Form = React.createClass({ // 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 () { @@ -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 @@ -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; }, {}); @@ -186,18 +187,18 @@ 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 () { @@ -205,30 +206,26 @@ Formsy.Form = React.createClass({ }, 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 @@ -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 @@ -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; @@ -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 }); @@ -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 () { diff --git a/src/utils.js b/src/utils.js index 074885d..16cbf09 100644 --- a/src/utils.js +++ b/src/utils.js @@ -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; } };