1)throw new Error("Formsy does not support multiple args on string validations. Use object format of validations instead.");return t[e]=n.length?n[0]:!0,t},{}):t||{}};i.exports={getInitialState:function(){return{_value:this.props.value,_isRequired:!1,_isValid:!0,_isPristine:!0,_pristineValue:this.props.value,_validationError:"",_externalError:null}},getDefaultProps:function(){return{validationError:"",validationErrors:{}}},componentWillMount:function(){var t=function(){this.setValidations(this.props.validations,this.props.required),this.props._attachToForm(this)}.bind(this);if(!this.props.name)throw new Error("Form Input requires a name property when used");return this.props._attachToForm?(t(),void 0):setTimeout(function(){if(this.isMounted()){if(!this.props._attachToForm)throw new Error("Form Mixin requires component to be nested in a Form");t()}}.bind(this),0)},componentWillReceiveProps:function(t){this.setValidations(t.validations,t.required)},componentDidUpdate:function(t){var i=function(){return this.props.value!==t.value&&this.state._value===t.value}.bind(this);i()&&this.setValue(this.props.value)},componentWillUnmount:function(){this.props._detachFromForm(this)},setValidations:function(t,i){this._validations=n(t)||{},this._requiredValidations=i===!0?{isDefaultRequiredValue:!0}:n(i)},setValue:function(t){this.setState({_value:t,_isPristine:!1},function(){this.props._validate(this)}.bind(this))},resetValue:function(){this.setState({_value:this.state._pristineValue,_isPristine:!0},function(){this.props._validate(this)})},getValue:function(){return this.state._value},hasValue:function(){return""!==this.state._value},getErrorMessage:function(){return!this.isValid()||this.showRequired()?this.state._externalError||this.state._validationError:null},isFormDisabled:function(){return this.props._isFormDisabled()},isValid:function(){return this.state._isValid},isPristine:function(){return this.state._isPristine},isRequired:function(){return!!this.props.required},showRequired:function(){return this.state._isRequired},showError:function(){return!this.showRequired()&&!this.isValid()},isValidValue:function(t){return this.props._isValidValue.call(null,this,t)}}},{}],3:[function(t,i){i.exports={arraysDiffer:function(t,i){var n=!1;return t.length!==i.length?n=!0:t.forEach(function(t,e){t!==i[e]&&(n=!0)}),n}}},{}],4:[function(t,i){i.exports={isDefaultRequiredValue:function(t,i){return void 0===i||""===i},hasValue:function(t,i){return!!i},matchRegexp:function(t,i,n){return!!i&&!!i.match(n)},isUndefined:function(t,i){return void 0===i},isEmptyString:function(t,i){return""===i},isEmail:function(t,i){return!i||i.match(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i)},isTrue:function(t,i){return i===!0},isFalse:function(t,i){return i===!1},isNumeric:function(t,i){if(!i)return!1;if("number"==typeof i)return!0;var n=i.match(/[-+]?(\d*[.])?\d+/);return n?n[0]==i:!1},isAlpha:function(t,i){return i&&/^[a-zA-Z]+$/.test(i)},isWords:function(t,i){return i&&/^[a-zA-Z\s]+$/.test(i)},isSpecialWords:function(t,i){return!i||i.match(/^[a-zA-Z\s\u00C0-\u017F]+$/)},isLength:function(t,i,n){return i&&i.length===n},equals:function(t,i,n){return i==n},equalsField:function(t,i,n){return i==t[n]},maxLength:function(t,i,n){return i&&i.length&&i.length<=n},minLength:function(t,i,n){return i&&i.length&&i.length>=n}}},{}]},{},[1]);
\ No newline at end of file
diff --git a/specs/Element-spec.jsx b/specs/Element-spec.jsx
index 8f7f1ec..c4c3c86 100644
--- a/specs/Element-spec.jsx
+++ b/specs/Element-spec.jsx
@@ -481,4 +481,41 @@ it('should allow an undefined value to be updated to a value', function (done) {
expect(inputComponent.isValid()).toBe(false);
});
+ it('should handle objects and arrays as values', function () {
+
+ var TestInput = React.createClass({
+ mixins: [Formsy.Mixin],
+ render: function () {
+ return {JSON.stringify(this.getValue())}
+ }
+ });
+ var TestForm = React.createClass({
+ getInitialState: function () {
+ return {
+ foo: {foo: 'bar'},
+ bar: ['foo']
+ };
+ },
+ render: function () {
+ return (
+
+
+
+
+ );
+ }
+ });
+ var form = TestUtils.renderIntoDocument();
+
+ form.setState({
+ foo: {foo: 'foo'},
+ bar: ['bar']
+ });
+
+ var inputs = TestUtils.scryRenderedComponentsWithType(form, TestInput);
+ expect(inputs[0].getValue()).toEqual({foo: 'foo'});
+ expect(inputs[1].getValue()).toEqual(['bar']);
+
+ });
+
});
diff --git a/src/Mixin.js b/src/Mixin.js
index 811cd74..dd886b0 100644
--- a/src/Mixin.js
+++ b/src/Mixin.js
@@ -1,3 +1,5 @@
+var utils = require('./utils.js');
+
var convertValidationsToObject = function (validations) {
if (typeof validations === 'string') {
@@ -77,11 +79,10 @@ module.exports = {
var isValueChanged = function () {
- return this.props.value !== prevProps.value && this.state._value === prevProps.value;
+ return !utils.isSame(this.props.value, prevProps.value) && utils.isSame(this.state._value, prevProps.value);
}.bind(this);
-
// If validations has changed or something outside changes
// the value, set the value again running a validation
if (isValueChanged()) {
diff --git a/src/utils.js b/src/utils.js
index 1b26f1b..be923ad 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,15 +1,42 @@
module.exports = {
- arraysDiffer: function (arrayA, arrayB) {
+ arraysDiffer: function (a, b) {
var isDifferent = false;
- if (arrayA.length !== arrayB.length) {
+ if (a.length !== b.length) {
isDifferent = true;
} else {
- arrayA.forEach(function (item, index) {
- if (item !== arrayB[index]) {
+ a.forEach(function (item, index) {
+ if (!this.isSame(item, b[index])) {
isDifferent = true;
}
- });
+ }, this);
}
return isDifferent;
+ },
+ objectsDiffer: function (a, b) {
+ var isDifferent = false;
+ if (Object.keys(a).length !== Object.keys(b).length) {
+ isDifferent = true;
+ } else {
+ Object.keys(a).forEach(function (key) {
+ if (!this.isSame(a[key], b[key])) {
+ isDifferent = true;
+ }
+ }, this);
+ }
+ return isDifferent;
+ },
+ isSame: function (a, b) {
+
+ if (a !== b) {
+ return false;
+ }
+
+ if (Array.isArray(a)) {
+ return !this.arraysDiffer(a, b);
+ } else if (typeof a === 'object' && a !== null) {
+ return !this.objectsDiffer(a, b);
+ }
+
+ return true;
}
};
diff --git a/webpack.config.js b/webpack.config.js
index e025a6a..dba29d5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -13,7 +13,8 @@ module.exports = {
module: {
loaders: [
- { test: /\.js$/, exclude: /node_modules/, loader: 'babel' }
+ { test: /\.js$/, exclude: /node_modules/, loader: 'babel' },
+ { test: /\.json$/, loader: 'json' }
]
}
diff --git a/webpack.production.config.js b/webpack.production.config.js
new file mode 100644
index 0000000..4826ea8
--- /dev/null
+++ b/webpack.production.config.js
@@ -0,0 +1,21 @@
+var path = require('path');
+
+module.exports = {
+
+ devtool: 'source-map',
+ entry: path.resolve(__dirname, 'src', 'main.js'),
+ externals: 'react',
+ output: {
+ path: path.resolve(__dirname, 'release'),
+ filename: 'formsy-react.js',
+ libraryTarget: 'umd',
+ library: 'Formsy'
+ },
+ module: {
+ loaders: [
+ { test: /\.js$/, exclude: /node_modules/, loader: 'babel' },
+ { test: /\.json$/, loader: 'json' }
+ ]
+ }
+
+};