-
+
-
+ Submit
);
}
@@ -119,7 +130,7 @@ The main concept is that forms, inputs and validation is done very differently a
This code results in a form with a submit button that will POST to /users when clicked. The submit button is disabled as long as the input is empty (required) or the value is not an email (isEmail). On validation error it will show the message: "This is not a valid email".
-#### This is an example of what you can enjoy building
+#### Building a form element
```javascript
/** @jsx React.DOM */
var Formsy = require('formsy-react');
@@ -163,10 +174,13 @@ So this is basically how you build your form elements. As you can see it is very
```javascript
Formsy.defaults({
contentType: 'urlencoded', // default: 'json'
+ headers: {} // default headers
+ /* DEPRECATED
hideSubmit: true, // default: false
submitButtonClass: 'btn btn-success', // default: null
cancelButtonClass: 'btn btn-default', // default: null
buttonWrapperClass: 'my-wrapper' // default: null
+ */
});
```
Use **defaults** to set general settings for all your forms.
@@ -199,30 +213,6 @@ Supports **json** (default) and **urlencoded** (x-www-form-urlencoded).
**Note!** Response has to be **json**.
-#### hideSubmit
-```html
-
-```
-Hides the submit button. Submit is done by ENTER on an input.
-
-#### submitButtonClass
-```html
-
-```
-Sets a class name on the submit button.
-
-#### cancelButtonClass
-```html
-
-```
-Sets a class name on the cancel button.
-
-#### buttonWrapperClass
-```html
-
-```
-Sets a class name on the container that wraps the **submit** and **cancel** buttons.
-
#### onSuccess(serverResponse)
```html
@@ -253,6 +243,18 @@ Will display a "cancel" button next to submit. On click it runs the function han
```
Takes a function to run when the server responds with an error http status code.
+#### onValid()
+```html
+
+```
+Whenever the form becomes valid the "onValid" handler is called. Use it to change state of buttons or whatever your heart desires.
+
+#### onInvalid()
+```html
+
+```
+Whenever the form becomes invalid the "onInvalid" handler is called. Use it to for example revert "onValid" state.
+
### Formsy.Mixin
#### name
@@ -342,7 +344,7 @@ var MyInput = React.createClass({
return (
- Reset
+ Reset
);
}
@@ -360,7 +362,7 @@ var MyInput = React.createClass({
return (
- {this.getErrorMessage}
+ {this.getErrorMessage()}
);
}
@@ -380,7 +382,7 @@ var MyInput = React.createClass({
{face}
- {this.getErrorMessage}
+ {this.getErrorMessage()}
);
}
@@ -399,7 +401,7 @@ var MyInput = React.createClass({
{this.props.label} {this.isRequired() ? '*' : null}
- {this.getErrorMessage}
+ {this.getErrorMessage()}
);
}
@@ -418,7 +420,7 @@ var MyInput = React.createClass({
return (
- {this.getErrorMessage}
+ {this.getErrorMessage()}
);
}
@@ -437,7 +439,7 @@ var MyInput = React.createClass({
return (
- {this.getErrorMessage}
+ {this.getErrorMessage()}
);
}
diff --git a/package.json b/package.json
index 75e480c..1f94d3c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "formsy-react",
- "version": "0.2.3",
+ "version": "0.3.0",
"description": "A form input builder and validator for React JS",
"main": "src/main.js",
"scripts": {
diff --git a/releases/0.3.0/formsy-react-0.3.0.js b/releases/0.3.0/formsy-react-0.3.0.js
new file mode 100755
index 0000000..582f758
--- /dev/null
+++ b/releases/0.3.0/formsy-react-0.3.0.js
@@ -0,0 +1,362 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Formsy=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= min && value.length <= max;
+ }
+ return value.length >= min;
+ },
+ equals: function (value, eql) {
+ return value == eql;
+ }
+};
+
+var toURLEncoded = function (element, key, list) {
+ var list = list || [];
+ if (typeof (element) == 'object') {
+ for (var idx in element)
+ toURLEncoded(element[idx], key ? key + '[' + idx + ']' : idx, list);
+ } else {
+ list.push(key + '=' + encodeURIComponent(element));
+ }
+ return list.join('&');
+};
+
+var request = function (method, url, data, contentType, headers) {
+
+ var contentType = contentType === 'urlencoded' ? 'application/' + contentType.replace('urlencoded', 'x-www-form-urlencoded') : 'application/json';
+ data = contentType === 'application/json' ? JSON.stringify(data) : toURLEncoded(data);
+
+ return new Promise(function (resolve, reject) {
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open(method, url, true);
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.setRequestHeader('Content-Type', contentType);
+
+ // Add passed headers
+ Object.keys(headers).forEach(function (header) {
+ xhr.setRequestHeader(header, headers[header]);
+ });
+
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4) {
+
+ if (xhr.status >= 200 && xhr.status < 300) {
+ resolve(xhr.responseText ? JSON.parse(xhr.responseText) : null);
+ } else {
+ reject(xhr.responseText ? JSON.parse(xhr.responseText) : null);
+ }
+
+ }
+ };
+ xhr.send(data);
+ } catch (e) {
+ reject(e);
+ }
+ });
+
+};
+var ajax = {
+ post: request.bind(null, 'POST'),
+ put: request.bind(null, 'PUT')
+};
+var options = {};
+
+Formsy.defaults = function (passedOptions) {
+ options = passedOptions;
+};
+
+Formsy.Mixin = {
+ getInitialState: function () {
+ return {
+ _value: this.props.value ? this.props.value : '',
+ _isValid: true
+ };
+ },
+ componentWillMount: function () {
+
+ if (!this.props.name) {
+ throw new Error('Form Input requires a name property when used');
+ }
+
+ if (!this.props._attachToForm) {
+ throw new Error('Form Mixin requires component to be nested in a Form');
+ }
+
+ if (this.props.required) {
+ this.props.validations = this.props.validations ? this.props.validations + ',' : '';
+ this.props.validations += 'isValue';
+ }
+ this.props._attachToForm(this);
+ },
+
+ // We have to make the validate method is kept when new props are added
+ componentWillReceiveProps: function (nextProps) {
+ nextProps._attachToForm = this.props._attachToForm;
+ nextProps._detachFromForm = this.props._detachFromForm;
+ nextProps._validate = this.props._validate;
+ },
+
+ // Detach it when component unmounts
+ componentWillUnmount: function () {
+ this.props._detachFromForm(this);
+ },
+
+ // We validate after the value has been set
+ setValue: function (value) {
+ this.setState({
+ _value: value
+ }, function () {
+ this.props._validate(this);
+ }.bind(this));
+ },
+ resetValue: function () {
+ this.setState({
+ _value: ''
+ }, function () {
+ this.props._validate(this);
+ });
+ },
+ getValue: function () {
+ return this.state._value;
+ },
+ hasValue: function () {
+ return this.state._value !== '';
+ },
+ getErrorMessage: function () {
+ return this.isValid() || this.showRequired() ? null : this.state._serverError || this.props.validationError;
+ },
+ isValid: function () {
+ return this.state._isValid;
+ },
+ isRequired: function () {
+ return this.props.required;
+ },
+ showRequired: function () {
+ return this.props.required && this.state._value === '';
+ },
+ showError: function () {
+ return !this.showRequired() && !this.state._isValid;
+ }
+};
+
+Formsy.addValidationRule = function (name, func) {
+ validationRules[name] = func;
+};
+
+Formsy.Form = React.createClass({
+ getInitialState: function () {
+ return {
+ isValid: true,
+ isSubmitting: false
+ };
+ },
+ getDefaultProps: function () {
+ return {
+ headers: {},
+ onSuccess: function () {},
+ onError: function () {},
+ onSubmit: function () {},
+ onSubmitted: function () {},
+ onValid: function () {},
+ onInvalid: function () {}
+ };
+ },
+
+ // 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.registerInputs(this.props.children);
+ },
+
+ componentDidMount: function () {
+ this.validateForm();
+ },
+
+ // Update model, submit to url prop and send the model
+ submit: function (event) {
+ event.preventDefault();
+
+ if (!this.props.url) {
+ throw new Error('Formsy Form needs a url property to post the form');
+ }
+
+ this.updateModel();
+ this.setState({
+ isSubmitting: true
+ });
+
+ this.props.onSubmit();
+
+ var headers = (Object.keys(this.props.headers).length && this.props.headers) || options.headers;
+ console.log('headers', headers);
+ ajax[this.props.method || 'post'](this.props.url, this.model, this.props.contentType || options.contentType || 'json', headers)
+ .then(function (response) {
+ this.onSuccess(response);
+ this.onSubmitted();
+ }.bind(this))
+ .catch(this.updateInputsWithError);
+ },
+
+ // 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));
+ },
+
+ // Go through errors from server and grab the components
+ // 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];
+ var args = [{
+ _isValid: false,
+ _serverError: errors[name]
+ }];
+ if (index === Object.keys(errors).length - 1) {
+ args.push(this.validateForm);
+ }
+ component.setState.apply(component, args);
+ }.bind(this));
+ this.setState({
+ isSubmitting: false
+ });
+ this.props.onError(errors);
+ this.props.onSubmitted();
+ },
+
+ // Traverse the children and children of children to find
+ // all inputs by checking the name prop. Maybe do a better
+ // check here
+ registerInputs: function (children) {
+ React.Children.forEach(children, function (child) {
+
+ if (child.props && child.props.name) {
+ child.props._attachToForm = this.attachToForm;
+ child.props._detachFromForm = this.detachFromForm;
+ child.props._validate = this.validate;
+ }
+
+ if (child.props && child.props.children) {
+ this.registerInputs(child.props.children);
+ }
+
+ }.bind(this));
+ },
+
+ // Use the binded values and the actual input value to
+ // validate the input and set its state. Then check the
+ // state of the form itself
+ validate: function (component) {
+
+ if (!component.props.validations) {
+ return;
+ }
+
+ // Run through the validations, split them up and call
+ // the validator IF there is a value or it is required
+ var isValid = true;
+ if (component.props.required || component.state._value !== '') {
+ component.props.validations.split(',').forEach(function (validation) {
+ var args = validation.split(':');
+ var validateMethod = args.shift();
+ args = args.map(function (arg) {
+ return JSON.parse(arg);
+ });
+ args = [component.state._value].concat(args);
+ if (!validationRules[validateMethod]) {
+ throw new Error('Formsy does not have the validation rule: ' + validateMethod);
+ }
+ if (!validationRules[validateMethod].apply(null, args)) {
+ isValid = false;
+ }
+ });
+ }
+
+ component.setState({
+ _isValid: isValid,
+ _serverError: null
+ }, this.validateForm);
+
+ },
+
+ // Validate the form by going through all child input components
+ // and check their state
+ validateForm: function () {
+ var allIsValid = true;
+ var inputs = this.inputs;
+
+ Object.keys(inputs).forEach(function (name) {
+ if (!inputs[name].state._isValid) {
+ allIsValid = false;
+ }
+ });
+
+ this.setState({
+ isValid: allIsValid
+ });
+
+ allIsValid && this.props.onValid();
+ !allIsValid && this.props.onInvalid();
+ },
+
+ // 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;
+ 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];
+ },
+ render: function () {
+
+ return React.DOM.form({
+ onSubmit: this.submit,
+ className: this.props.className
+ },
+ this.props.children
+ );
+
+ }
+});
+
+if (!global.exports && !global.module && (!global.define || !global.define.amd)) {
+ global.Formsy = Formsy;
+}
+
+module.exports = Formsy;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"react":"react"}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/releases/0.3.0/formsy-react-0.3.0.min.js b/releases/0.3.0/formsy-react-0.3.0.min.js
new file mode 100755
index 0000000..2ae5aed
--- /dev/null
+++ b/releases/0.3.0/formsy-react-0.3.0.min.js
@@ -0,0 +1 @@
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.Formsy=t()}}(function(){return function t(e,i,n){function o(s,u){if(!i[s]){if(!e[s]){var a="function"==typeof require&&require;if(!u&&a)return a(s,!0);if(r)return r(s,!0);var p=new Error("Cannot find module '"+s+"'");throw p.code="MODULE_NOT_FOUND",p}var F=i[s]={exports:{}};e[s][0].call(F.exports,function(t){var i=e[s][1][t];return o(i?i:t)},F,F.exports,t,e,i,n)}return i[s].exports}for(var r="function"==typeof require&&require,s=0;s=e&&t.length<=i:t.length>=e},equals:function(t,e){return t==e}},s=function(t,e,i){var i=i||[];if("object"==typeof t)for(var n in t)s(t[n],e?e+"["+n+"]":n,i);else i.push(e+"="+encodeURIComponent(t));return i.join("&")},u=function(t,e,i,n,o){var n="urlencoded"===n?"application/"+n.replace("urlencoded","x-www-form-urlencoded"):"application/json";return i="application/json"===n?JSON.stringify(i):s(i),new Promise(function(r,s){try{var u=new XMLHttpRequest;u.open(t,e,!0),u.setRequestHeader("Accept","application/json"),u.setRequestHeader("Content-Type",n),Object.keys(o).forEach(function(t){u.setRequestHeader(t,o[t])}),u.onreadystatechange=function(){4===u.readyState&&(u.status>=200&&u.status<300?r(u.responseText?JSON.parse(u.responseText):null):s(u.responseText?JSON.parse(u.responseText):null))},u.send(i)}catch(a){s(a)}})},a={post:u.bind(null,"POST"),put:u.bind(null,"PUT")},p={};o.defaults=function(t){p=t},o.Mixin={getInitialState:function(){return{_value:this.props.value?this.props.value:"",_isValid:!0}},componentWillMount:function(){if(!this.props.name)throw new Error("Form Input requires a name property when used");if(!this.props._attachToForm)throw new Error("Form Mixin requires component to be nested in a Form");this.props.required&&(this.props.validations=this.props.validations?this.props.validations+",":"",this.props.validations+="isValue"),this.props._attachToForm(this)},componentWillReceiveProps:function(t){t._attachToForm=this.props._attachToForm,t._detachFromForm=this.props._detachFromForm,t._validate=this.props._validate},componentWillUnmount:function(){this.props._detachFromForm(this)},setValue:function(t){this.setState({_value:t},function(){this.props._validate(this)}.bind(this))},resetValue:function(){this.setState({_value:""},function(){this.props._validate(this)})},getValue:function(){return this.state._value},hasValue:function(){return""!==this.state._value},getErrorMessage:function(){return this.isValid()||this.showRequired()?null:this.state._serverError||this.props.validationError},isValid:function(){return this.state._isValid},isRequired:function(){return this.props.required},showRequired:function(){return this.props.required&&""===this.state._value},showError:function(){return!this.showRequired()&&!this.state._isValid}},o.addValidationRule=function(t,e){r[t]=e},o.Form=n.createClass({getInitialState:function(){return{isValid:!0,isSubmitting:!1}},getDefaultProps:function(){return{headers:{},onSuccess:function(){},onError:function(){},onSubmit:function(){},onSubmitted:function(){},onValid:function(){},onInvalid:function(){}}},componentWillMount:function(){this.inputs={},this.model={},this.registerInputs(this.props.children)},componentDidMount:function(){this.validateForm()},submit:function(t){if(t.preventDefault(),!this.props.url)throw new Error("Formsy Form needs a url property to post the form");this.updateModel(),this.setState({isSubmitting:!0}),this.props.onSubmit();var e=Object.keys(this.props.headers).length&&this.props.headers||p.headers;console.log("headers",e),a[this.props.method||"post"](this.props.url,this.model,this.props.contentType||p.contentType||"json",e).then(function(t){this.onSuccess(t),this.onSubmitted()}.bind(this)).catch(this.updateInputsWithError)},updateModel:function(){Object.keys(this.inputs).forEach(function(t){var e=this.inputs[t];this.model[t]=e.state._value}.bind(this))},updateInputsWithError:function(t){Object.keys(t).forEach(function(e,i){var n=this.inputs[e],o=[{_isValid:!1,_serverError:t[e]}];i===Object.keys(t).length-1&&o.push(this.validateForm),n.setState.apply(n,o)}.bind(this)),this.setState({isSubmitting:!1}),this.props.onError(t),this.props.onSubmitted()},registerInputs:function(t){n.Children.forEach(t,function(t){t.props&&t.props.name&&(t.props._attachToForm=this.attachToForm,t.props._detachFromForm=this.detachFromForm,t.props._validate=this.validate),t.props&&t.props.children&&this.registerInputs(t.props.children)}.bind(this))},validate:function(t){if(t.props.validations){var e=!0;(t.props.required||""!==t.state._value)&&t.props.validations.split(",").forEach(function(i){var n=i.split(":"),o=n.shift();if(n=n.map(function(t){return JSON.parse(t)}),n=[t.state._value].concat(n),!r[o])throw new Error("Formsy does not have the validation rule: "+o);r[o].apply(null,n)||(e=!1)}),t.setState({_isValid:e,_serverError:null},this.validateForm)}},validateForm:function(){var t=!0,e=this.inputs;Object.keys(e).forEach(function(i){e[i].state._isValid||(t=!1)}),this.setState({isValid:t}),t&&this.props.onValid(),!t&&this.props.onInvalid()},attachToForm:function(t){this.inputs[t.props.name]=t,this.model[t.props.name]=t.state._value,this.validate(t)},detachFromForm:function(t){delete this.inputs[t.props.name],delete this.model[t.props.name]},render:function(){return n.DOM.form({onSubmit:this.submit,className:this.props.className},this.props.children)}}),i.exports||i.module||i.define&&i.define.amd||(i.Formsy=o),e.exports=o}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{react:"react"}]},{},[1])(1)});
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 95455bc..e9d2196 100644
--- a/src/main.js
+++ b/src/main.js
@@ -26,18 +26,19 @@ var validationRules = {
return value == eql;
}
};
-var toURLEncoded = function (element,key,list){
+
+var toURLEncoded = function (element, key, list) {
var list = list || [];
- if(typeof(element)=='object'){
+ if (typeof (element) == 'object') {
for (var idx in element)
- toURLEncoded(element[idx],key?key+'['+idx+']':idx,list);
+ toURLEncoded(element[idx], key ? key + '[' + idx + ']' : idx, list);
} else {
- list.push(key+'='+encodeURIComponent(element));
+ list.push(key + '=' + encodeURIComponent(element));
}
return list.join('&');
};
-var request = function (method, url, data, contentType) {
+var request = function (method, url, data, contentType, headers) {
var contentType = contentType === 'urlencoded' ? 'application/' + contentType.replace('urlencoded', 'x-www-form-urlencoded') : 'application/json';
data = contentType === 'application/json' ? JSON.stringify(data) : toURLEncoded(data);
@@ -48,6 +49,12 @@ var request = function (method, url, data, contentType) {
xhr.open(method, url, true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', contentType);
+
+ // Add passed headers
+ Object.keys(headers).forEach(function (header) {
+ xhr.setRequestHeader(header, headers[header]);
+ });
+
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
@@ -78,26 +85,26 @@ Formsy.defaults = function (passedOptions) {
Formsy.Mixin = {
getInitialState: function () {
- return {
- _value: this.props.value ? this.props.value : '',
- _isValid: true
- };
+ return {
+ _value: this.props.value ? this.props.value : '',
+ _isValid: true
+ };
},
componentWillMount: function () {
- if (!this.props.name) {
- throw new Error('Form Input requires a name property when used');
- }
+ if (!this.props.name) {
+ throw new Error('Form Input requires a name property when used');
+ }
- if (!this.props._attachToForm) {
- throw new Error('Form Mixin requires component to be nested in a Form');
- }
+ if (!this.props._attachToForm) {
+ throw new Error('Form Mixin requires component to be nested in a Form');
+ }
- if (this.props.required) {
- this.props.validations = this.props.validations ? this.props.validations + ',' : '';
- this.props.validations += 'isValue';
- }
- this.props._attachToForm(this);
+ if (this.props.required) {
+ this.props.validations = this.props.validations ? this.props.validations + ',' : '';
+ this.props.validations += 'isValue';
+ }
+ this.props._attachToForm(this);
},
// We have to make the validate method is kept when new props are added
@@ -109,16 +116,16 @@ Formsy.Mixin = {
// Detach it when component unmounts
componentWillUnmount: function () {
- this.props._detachFromForm(this);
+ this.props._detachFromForm(this);
},
// We validate after the value has been set
setValue: function (value) {
- this.setState({
- _value: value
- }, function () {
- this.props._validate(this);
- }.bind(this));
+ this.setState({
+ _value: value
+ }, function () {
+ this.props._validate(this);
+ }.bind(this));
},
resetValue: function () {
this.setState({
@@ -163,11 +170,14 @@ Formsy.Form = React.createClass({
},
getDefaultProps: function () {
return {
+ headers: {},
onSuccess: function () {},
onError: function () {},
onSubmit: function () {},
- onSubmitted: function () {}
- }
+ onSubmitted: function () {},
+ onValid: function () {},
+ onInvalid: function () {}
+ };
},
// Add a map to store the inputs of the form, a model to store
@@ -194,8 +204,12 @@ Formsy.Form = React.createClass({
this.setState({
isSubmitting: true
});
+
this.props.onSubmit();
- ajax[this.props.method || 'post'](this.props.url, this.model, this.props.contentType || options.contentType || 'json')
+
+ var headers = (Object.keys(this.props.headers).length && this.props.headers) || options.headers;
+ console.log('headers', headers);
+ ajax[this.props.method || 'post'](this.props.url, this.model, this.props.contentType || options.contentType || 'json', headers)
.then(function (response) {
this.onSuccess(response);
this.onSubmitted();
@@ -269,7 +283,9 @@ Formsy.Form = React.createClass({
component.props.validations.split(',').forEach(function (validation) {
var args = validation.split(':');
var validateMethod = args.shift();
- args = args.map(function (arg) { return JSON.parse(arg); });
+ args = args.map(function (arg) {
+ return JSON.parse(arg);
+ });
args = [component.state._value].concat(args);
if (!validationRules[validateMethod]) {
throw new Error('Formsy does not have the validation rule: ' + validateMethod);
@@ -292,7 +308,7 @@ Formsy.Form = React.createClass({
validateForm: function () {
var allIsValid = true;
var inputs = this.inputs;
-
+
Object.keys(inputs).forEach(function (name) {
if (!inputs[name].state._isValid) {
allIsValid = false;
@@ -302,6 +318,9 @@ Formsy.Form = React.createClass({
this.setState({
isValid: allIsValid
});
+
+ allIsValid && this.props.onValid();
+ !allIsValid && this.props.onInvalid();
},
// Method put on each input component to register
@@ -319,30 +338,14 @@ Formsy.Form = React.createClass({
delete this.model[component.props.name];
},
render: function () {
- var submitButton = React.DOM.button({
- className: this.props.submitButtonClass || options.submitButtonClass,
- disabled: this.state.isSubmitting || !this.state.isValid
- }, this.props.submitLabel || 'Submit');
-
- var cancelButton = React.DOM.button({
- onClick: this.props.onCancel,
- disabled: this.state.isSubmitting,
- className: this.props.cancelButtonClass || options.cancelButtonClass
- }, this.props.cancelLabel || 'Cancel');
return React.DOM.form({
- onSubmit: this.submit,
- className: this.props.className
- },
- this.props.children,
- React.DOM.div({
- className: this.props.buttonWrapperClass || options.buttonWrapperClass
- },
- this.props.onCancel ? cancelButton : null,
- this.props.hideSubmit || options.hideSubmit ? null : submitButton
- )
+ onSubmit: this.submit,
+ className: this.props.className
+ },
+ this.props.children
);
-
+
}
});
@@ -350,4 +353,4 @@ if (!global.exports && !global.module && (!global.define || !global.define.amd))
global.Formsy = Formsy;
}
-module.exports = Formsy;
\ No newline at end of file
+module.exports = Formsy;