From c25d3b2e14fe0f1194ce8e7a8a61f7517867790b Mon Sep 17 00:00:00 2001 From: SimaQ Date: Wed, 29 Jul 2015 10:32:01 +0800 Subject: [PATCH 01/12] update form validation style --- style/components/form.less | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/style/components/form.less b/style/components/form.less index 36e64e8fa4..ddbd2c179d 100644 --- a/style/components/form.less +++ b/style/components/form.less @@ -260,15 +260,54 @@ form { } // Validation state +.has-success, .has-warning, .has-error, .is-validating { + &.has-feedback:after { + position: absolute; + bottom: 0; + right: 0; + font-family: "anticon" !important; + width: 32px; + height: 32px; + line-height: 32px; + text-align: center; + font-size: 14px; + } +} + .has-success { .form-control-validation(@success-color; @input-hover-border-color;); .@{css-prefix}input { border-color: @input-border-color; } + + &.has-feedback:after { + content: '\e614'; + color: @success-color; + } } + .has-warning { .form-control-validation(@warning-color; @warning-color;); + + &.has-feedback:after { + content: '\e628'; + color: @warning-color; + } } + .has-error { .form-control-validation(@error-color; @error-color;); + + &.has-feedback:after { + content: '\e628'; + color: @error-color; + } +} + +.is-validating { + &.has-feedback:after { + display: inline-block; + .animation(loadingCircle 1s infinite linear); + content:"\e610"; + } } From 7653aab6bcf04228b09186b4c79dd0b9b5cbda54 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Wed, 29 Jul 2015 10:32:21 +0800 Subject: [PATCH 02/12] update form demo --- components/form/demo/validate.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/components/form/demo/validate.md b/components/form/demo/validate.md index 26d760a7a1..98ac20e4c0 100644 --- a/components/form/demo/validate.md +++ b/components/form/demo/validate.md @@ -39,41 +39,37 @@
-
+
- +
信息审核中...
-
信息审核中...
-
+
-
+
-
-
+
-
+
- +
请输入数字和字母组合
-
请输入数字和字母组合
-
+
-
+
-
From d97da23e3a8143849182730e85632a9d821531d0 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Wed, 29 Jul 2015 10:32:55 +0800 Subject: [PATCH 03/12] add form-validation demo --- components/validation/demo/basic.md | 153 ++++++++++++++++++++++++++++ components/validation/index.jsx | 3 + components/validation/index.md | 21 ++++ index.js | 3 +- package.json | 1 + 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 components/validation/demo/basic.md create mode 100644 components/validation/index.jsx create mode 100644 components/validation/index.md diff --git a/components/validation/demo/basic.md b/components/validation/demo/basic.md new file mode 100644 index 0000000000..dd2e66d986 --- /dev/null +++ b/components/validation/demo/basic.md @@ -0,0 +1,153 @@ +# 基本 + +- order: 0 + +表单校验实例 + +--- + +````jsx +var Validation = antd.Validation; +var Validator = Validation.Validator; + +function toNumber(v) { + var num = Number(v); + // num === ' ' + if (!isNaN(num)) { + num = parseInt(v); + } + return isNaN(num) ? v : num; +} + +function cx(classNames) { + if (typeof classNames === 'object') { + return Object.keys(classNames).filter(function(className) { + return classNames[className]; + }).join(' '); + } else { + return Array.prototype.join.call(arguments, ' '); + } +} + +var Form = React.createClass({ + mixins: [Validation.FieldMixin], + + getInitialState() { + return { + status: { + number: {}, + pass: {}, + pass2: {}, + blurNumber: {}, + optionalNumber: {}, + name: {}, + must: {} + }, + formData: { + number: 0, + pass: undefined, + pass2: undefined, + blurNumber: undefined, + optionalNumber: undefined, + name: undefined, + must: undefined + } + }; + }, + + handleReset(e) { + this.refs.validation.reset(); + this.setState(this.getInitialState()); + e.preventDefault(); + }, + + handleSubmit(e) { + e.preventDefault(); + var validation = this.refs.validation; + validation.validate((valid) => { + if (!valid) { + console.log('error in form'); + return; + } else { + console.log('submit'); + } + console.log(this.state.formData); + }); + }, + + userExists(rule, value, callback) { + + if (!value) { + callback(); + } else { + setTimeout(function () { + if (value === '1') { + callback([new Error('are you kidding?')]); + } else if (value === 'yiminghe') { + callback([new Error('forbid yiminghe')]); + } else { + callback(); + } + }, 1000); + } + }, + + checkPass(rule, value, callback) { + if (this.state.formData.pass2) { + this.refs.validation.forceValidate(['pass2']); + } + callback(); + }, + + checkPass2(rule, value, callback) { + if (value !== this.state.formData.pass) { + callback('two password are not same!'); + } else { + callback(); + } + }, + + render() { + var formData = this.state.formData; + var status = this.state.status; + + var nameClasses = cx({ + 'has-feedback': true, + 'has-error': status.name.errors, + 'is-validating': status.name.isValidating, + 'has-success': formData.name && !status.name.errors && !status.name.isValidating + }); + + return ( +
+ +
+ +
+
+ + + + {status.name.isValidating ?
信息审核中...
: null} + {status.name.errors ?
{status.name.errors.join(',')}
: null} +
+
+
+ +
+
+ +     + 重 置 +
+
+
+
+ ); + } +}); + +React.render( +
+, document.getElementById('components-validation-demo-basic')); +```` diff --git a/components/validation/index.jsx b/components/validation/index.jsx new file mode 100644 index 0000000000..00589d9285 --- /dev/null +++ b/components/validation/index.jsx @@ -0,0 +1,3 @@ +import Validation from 'rc-form-validation'; + +export default Validation; diff --git a/components/validation/index.md b/components/validation/index.md new file mode 100644 index 0000000000..b1d89a6f9b --- /dev/null +++ b/components/validation/index.md @@ -0,0 +1,21 @@ +# Validation + +- category: Components +- chinese: 表单校验 +- order: 19 + +--- + +表单校验。 + +## 何时使用 + +需要对表单域进行校验时。 + +## API + +属性如下 + +| 成员 | 说明 | 类型 | 默认值 | +|-------------|----------------|--------------------|--------------| + diff --git a/index.js b/index.js index 78dad62408..28a70941f0 100644 --- a/index.js +++ b/index.js @@ -23,7 +23,8 @@ var antd = { message: require('./components/message'), Slider: require('./components/slider'), Radio: require('./components/radio'), - RadioGroup: require('./components/radio/group') + RadioGroup: require('./components/radio/group'), + Validation: require('./components/validation') }; module.exports = antd; diff --git a/package.json b/package.json index 88e44618cf..cbc2a2aaab 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "rc-collapse": "~1.2.3", "rc-dialog": "~4.4.0", "rc-dropdown": "~1.1.1", + "rc-form-validation": "^2.4.7", "rc-input-number": "~2.0.1", "rc-menu": "~3.4.2", "rc-notification": "~1.0.1", From 9718bc9de7558ee256dbce95a8bd94c900705168 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Wed, 29 Jul 2015 19:14:44 +0800 Subject: [PATCH 04/12] update form validate stylr --- style/components/form.less | 22 ++++++++++++++++++++++ style/mixins/form.less | 6 ++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/style/components/form.less b/style/components/form.less index ddbd2c179d..5ca117e765 100644 --- a/style/components/form.less +++ b/style/components/form.less @@ -278,6 +278,7 @@ form { .form-control-validation(@success-color; @input-hover-border-color;); .@{css-prefix}input { border-color: @input-border-color; + box-shadow: none; } &.has-feedback:after { @@ -293,6 +294,16 @@ form { content: '\e628'; color: @warning-color; } + + .@{selectPrefixCls} { + &-selection { + border-color: @warning-color; + box-shadow: 0 0 0 2px tint(@warning-color, 80%); + } + &-arrow { + color: @warning-color; + } + } } .has-error { @@ -302,6 +313,17 @@ form { content: '\e628'; color: @error-color; } + + .@{selectPrefixCls} { + &-selection { + border-color: @error-color; + box-shadow: 0 0 0 2px tint(@error-color, 80%); + } + + &-arrow { + color: @error-color; + } + } } .is-validating { diff --git a/style/mixins/form.less b/style/mixins/form.less index bf9ffddade..edd5b36c97 100644 --- a/style/mixins/form.less +++ b/style/mixins/form.less @@ -5,10 +5,8 @@ // 输入框的不同校验状态 .@{css-prefix}input { border-color: @border-color; - &:focus { - border-color: @border-color; - box-shadow: 0 0 0 2px tint(@border-color, 80%); - } + box-shadow: 0 0 0 2px tint(@border-color, 80%); + &:not([disabled]):hover { border-color: @border-color; } From 9cda1103e875e1de02c6264d56c34df5c832d5a7 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Wed, 29 Jul 2015 19:14:59 +0800 Subject: [PATCH 05/12] update demo --- components/validation/demo/basic.md | 118 ++++++++++++++++++---------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/components/validation/demo/basic.md b/components/validation/demo/basic.md index dd2e66d986..e2e0c27c4d 100644 --- a/components/validation/demo/basic.md +++ b/components/validation/demo/basic.md @@ -9,6 +9,8 @@ ````jsx var Validation = antd.Validation; var Validator = Validation.Validator; +var Select = antd.Select; +var Option = Select.Option; function toNumber(v) { var num = Number(v); @@ -29,32 +31,45 @@ function cx(classNames) { } } + var Form = React.createClass({ mixins: [Validation.FieldMixin], getInitialState() { return { status: { - number: {}, - pass: {}, - pass2: {}, - blurNumber: {}, - optionalNumber: {}, + must: {}, + email: {}, name: {}, - must: {} + select: {}, + startDate: {}, + endDate: {} }, formData: { - number: 0, - pass: undefined, - pass2: undefined, - blurNumber: undefined, - optionalNumber: undefined, + must: undefined, + email: undefined, name: undefined, - must: undefined + select: undefined, + startDate: undefined, + endDate: undefined } }; }, + validateStyle(item, hasFeedback=true) { + var formData = this.state.formData; + var status = this.state.status; + + var classes = cx({ + 'has-feedback': hasFeedback, + 'has-error': status[item].errors, + 'is-validating': status[item].isValidating, + 'has-success': formData[item] && !status[item].errors && !status[item].isValidating + }); + + return classes; + }, + handleReset(e) { this.refs.validation.reset(); this.setState(this.getInitialState()); @@ -76,15 +91,12 @@ var Form = React.createClass({ }, userExists(rule, value, callback) { - if (!value) { callback(); } else { setTimeout(function () { - if (value === '1') { - callback([new Error('are you kidding?')]); - } else if (value === 'yiminghe') { - callback([new Error('forbid yiminghe')]); + if (value === 'yiminghe') { + callback([new Error('抱歉,该用户名已被占用。')]); } else { callback(); } @@ -92,48 +104,68 @@ var Form = React.createClass({ } }, - checkPass(rule, value, callback) { - if (this.state.formData.pass2) { - this.refs.validation.forceValidate(['pass2']); - } - callback(); - }, - - checkPass2(rule, value, callback) { - if (value !== this.state.formData.pass) { - callback('two password are not same!'); - } else { - callback(); - } - }, - render() { var formData = this.state.formData; var status = this.state.status; - - var nameClasses = cx({ - 'has-feedback': true, - 'has-error': status.name.errors, - 'is-validating': status.name.isValidating, - 'has-success': formData.name && !status.name.errors && !status.name.isValidating - }); return ( +
+ +
+
+ + + + {status.must.errors ?
{status.must.errors.join(',')}
: null} +
+
+
+ +
+ +
+
+ + + + {status.email.isValidating ?
正在校验中...
: null} + {status.email.errors ?
{status.email.errors.join(',')}
: null} +
+
+
+
-
- - +
+ + - {status.name.isValidating ?
信息审核中...
: null} + {status.name.isValidating ?
正在校验中...
: null} {status.name.errors ?
{status.name.errors.join(',')}
: null}
+
+ +
+
+ + + + {status.select.errors ?
{status.select.errors.join(',')}
: null} +
+
+
+
From d6e0678fba04c3816f0373f968f9daad4a172182 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Thu, 30 Jul 2015 20:24:43 +0800 Subject: [PATCH 06/12] update validation basic demo --- components/validation/demo/basic.md | 199 ++++++++++++++++++---------- 1 file changed, 126 insertions(+), 73 deletions(-) diff --git a/components/validation/demo/basic.md b/components/validation/demo/basic.md index e2e0c27c4d..ee7621f0de 100644 --- a/components/validation/demo/basic.md +++ b/components/validation/demo/basic.md @@ -2,7 +2,9 @@ - order: 0 -表单校验实例 +表单校验。 + +为每一个 Validator 定义 rules 以及 validator,rules 对应 Validation 提供的原生校验规则(通过定义 rules 中的 message 属性可以自定义错误信息的提示,详见 [async-validator](https://github.com/yiminghe/async-validator)),而 validator 为用户提供自定义的校验规则, --- @@ -11,15 +13,8 @@ var Validation = antd.Validation; var Validator = Validation.Validator; var Select = antd.Select; var Option = Select.Option; - -function toNumber(v) { - var num = Number(v); - // num === ' ' - if (!isNaN(num)) { - num = parseInt(v); - } - return isNaN(num) ? v : num; -} +var Radio = antd.Radio; +var RadioGroup = antd.RadioGroup; function cx(classNames) { if (typeof classNames === 'object') { @@ -31,27 +26,28 @@ function cx(classNames) { } } - var Form = React.createClass({ mixins: [Validation.FieldMixin], getInitialState() { return { status: { - must: {}, email: {}, name: {}, select: {}, - startDate: {}, - endDate: {} + radio: {}, + passwd: {}, + rePasswd: {}, + textarea: {} }, formData: { - must: undefined, email: undefined, name: undefined, select: undefined, - startDate: undefined, - endDate: undefined + radio: undefined, + passwd: undefined, + rePasswd: undefined, + textarea: undefined } }; }, @@ -104,82 +100,139 @@ var Form = React.createClass({ } }, + checkPass(rule, value, callback) { + if (this.state.formData.passwd) { + this.refs.validation.forceValidate(['rePasswd']); + } + callback(); + }, + + checkPass2(rule, value, callback) { + if (value !== this.state.formData.passwd) { + callback('两次输入密码不一致!'); + } else { + callback(); + } + }, + render() { var formData = this.state.formData; var status = this.state.status; return ( - - -
- -
-
- - - - {status.must.errors ?
{status.must.errors.join(',')}
: null} + + +
+ +
+
+ + + + {status.name.isValidating ?
正在校验中...
: null} + {status.name.errors ?
{status.name.errors.join(',')}
: null} +
-
-
- -
-
- - - - {status.email.isValidating ?
正在校验中...
: null} - {status.email.errors ?
{status.email.errors.join(',')}
: null} +
+ +
+
+ + + + {status.email.errors ?
{status.email.errors.join(',')}
: null} +
-
-
- -
-
- - - - {status.name.isValidating ?
正在校验中...
: null} - {status.name.errors ?
{status.name.errors.join(',')}
: null} +
+ +
+
+ + + + {status.select.errors ?
{status.select.errors.join(',')}
: null} +
+
+
+ +
+ +
+
+ + + + + + + {status.radio.errors ?
{status.radio.errors.join(',')}
: null} +
-
-
- -
-
- - - - {status.select.errors ?
{status.select.errors.join(',')}
: null} +
+ +
+
+ + + + {status.passwd.errors ?
{status.passwd.errors.join(',')}
: null} +
-
-
-
- -     - 重 置 +
+ +
+
+ + + + {status.rePasswd.errors ?
{status.rePasswd.errors.join(', ')}
: null} +
+
-
- - + + +
+ +
+
+ + + + {status.textarea.errors ?
{status.textarea.errors.join(',')}
: null} +
+
+
+ +
+
+ +     + 重 置 +
+
+ + ); } }); -React.render( -
-, document.getElementById('components-validation-demo-basic')); +React.render(, document.getElementById('components-validation-demo-basic')); ```` From d2d5efa7233f21a38688bc1a0a29da05982dd5c6 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Fri, 31 Jul 2015 10:24:56 +0800 Subject: [PATCH 07/12] update validate style --- style/components/form.less | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/style/components/form.less b/style/components/form.less index 5ca117e765..2c08df511b 100644 --- a/style/components/form.less +++ b/style/components/form.less @@ -295,6 +295,7 @@ form { color: @warning-color; } + // ant-select .@{selectPrefixCls} { &-selection { border-color: @warning-color; @@ -304,6 +305,11 @@ form { color: @warning-color; } } + + // ant-datepicker + .@{prefixCalendarClass}-picker-icon:after { + color: @warning-color; + } } .has-error { @@ -313,7 +319,8 @@ form { content: '\e628'; color: @error-color; } - + + // ant-select .@{selectPrefixCls} { &-selection { border-color: @error-color; @@ -324,6 +331,11 @@ form { color: @error-color; } } + + // ant-datepicker + .@{prefixCalendarClass}-picker-icon:after { + color: @error-color; + } } .is-validating { From 4a2159800fd2b57b1a60578e61f22f8e24630789 Mon Sep 17 00:00:00 2001 From: SimaQ Date: Fri, 31 Jul 2015 10:40:17 +0800 Subject: [PATCH 08/12] add password validation demo --- components/validation/demo/customize.md | 249 ++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 components/validation/demo/customize.md diff --git a/components/validation/demo/customize.md b/components/validation/demo/customize.md new file mode 100644 index 0000000000..be71cf5ce7 --- /dev/null +++ b/components/validation/demo/customize.md @@ -0,0 +1,249 @@ +# 自定义校验规则 + +- order: 1 + +密码校验实例。 + +--- + +````jsx +var Validation = antd.Validation; +var Validator = Validation.Validator; + +function cx(classNames) { + if (typeof classNames === 'object') { + return Object.keys(classNames).filter(function(className) { + return classNames[className]; + }).join(' '); + } else { + return Array.prototype.join.call(arguments, ' '); + } +} + +var Form = React.createClass({ + mixins: [Validation.FieldMixin], + + getInitialState() { + return { + status: { + pass: {}, + rePass: {} + }, + formData: { + pass: undefined, + rePass: undefined + }, + passBarShow: false, // 是否显示密码强度提示条 + rePassBarShow: false, + passStrength: 'L', // 密码强度 + rePassStrength: 'L' + }; + }, + + handleSubmit(e) { + e.preventDefault(); + var validation = this.refs.validation; + validation.validate((valid) => { + if (!valid) { + console.log('error in form'); + return; + } else { + console.log('submit'); + } + console.log(this.state.formData); + }); + }, + + handleReset(e) { + this.refs.validation.reset(); + this.setState(this.getInitialState()); + e.preventDefault(); + }, + + renderValidateStyle(item, hasFeedback=true) { + var formData = this.state.formData; + var status = this.state.status; + + var classes = cx({ + 'has-feedback': hasFeedback, + 'has-error': status[item].errors, + 'is-validating': status[item].isValidating, + 'has-success': formData[item] && !status[item].errors && !status[item].isValidating + }); + + return classes; + }, + + getPassStrenth(value, type) { + if (value) { + var strength; + // 密码强度的校验规则自定义,这里只是做个简单的示例 + if (value.length < 6) { + strength = 'L'; + } else if (value.length <= 9) { + strength = 'M'; + } else { + strength = 'H'; + } + type === 'pass' ? this.setState({ passBarShow: true, passStrength: strength }) : this.setState({ rePassBarShow: true, rePassStrength: strength }); + } else { + type === 'pass' ? this.setState({ passBarShow: false }) : this.setState({ rePassBarShow: false }); + } + }, + + checkPass(rule, value, callback) { + this.getPassStrenth(value, 'pass'); + + if (this.state.formData.pass) { + this.refs.validation.forceValidate(['rePass']); + } + + callback(); + }, + + checkPass2(rule, value, callback) { + this.getPassStrenth(value, 'rePass'); + + if (value && value !== this.state.formData.pass) { + callback('两次输入密码不一致!'); + } else { + callback(); + } + }, + + renderPassStrengthBar(type) { + var strength = type === 'pass' ? this.state.passStrength : this.state.rePassStrength; + var classSet = cx({ + 'ant-pwd-strength': true, + 'ant-pwd-strength-low': strength === 'L', + 'ant-pwd-strength-medium': strength === 'M', + 'ant-pwd-strength-high': strength === 'H' + }); + var level = { + L: '低', + M: '中', + H: '高' + }; + + return ( +
+
    +
  • +
  • +
  • + + {level[strength]} + +
+
+ ); + }, + + render() { + var formData = this.state.formData; + var status = this.state.status; + + return ( + + + +
+ +
+
+ + + + {status.pass.errors ?
{status.pass.errors.join(',')}
: null} +
+
+
+ {this.state.passBarShow ? this.renderPassStrengthBar('pass') : null} +
+
+ +
+ +
+
+ + + + {status.rePass.errors ?
{status.rePass.errors.join(', ')}
: null} +
+
+
+ {this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null} +
+
+ +
+
+ +     + 重 置 +
+
+
+ + ); + } +}); + +React.render(
, document.getElementById('components-validation-demo-customize')); +```` + + From ed43331251867ce46c8a6d346d2af6693082fdcd Mon Sep 17 00:00:00 2001 From: SimaQ Date: Fri, 31 Jul 2015 16:03:24 +0800 Subject: [PATCH 09/12] update instruction --- components/validation/demo/basic.md | 6 ++- components/validation/index.md | 59 +++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/components/validation/demo/basic.md b/components/validation/demo/basic.md index ee7621f0de..271534b8d2 100644 --- a/components/validation/demo/basic.md +++ b/components/validation/demo/basic.md @@ -2,9 +2,11 @@ - order: 0 -表单校验。 +基本的表单校验栗子。 -为每一个 Validator 定义 rules 以及 validator,rules 对应 Validation 提供的原生校验规则(通过定义 rules 中的 message 属性可以自定义错误信息的提示,详见 [async-validator](https://github.com/yiminghe/async-validator)),而 validator 为用户提供自定义的校验规则, +每个表单域要声明 `name` 属性作为校验的标识,可通过其 `isValidating` `errors` 属性判断是否处于校验中、是否校验不通过状态,具体可参见 **用户名** 校验。 + +表单提交的时候,通过 Validation 的 validate 方法判断是否所有表单域校验通过(isValid 会作为回调函数的参数传入)。 --- diff --git a/components/validation/index.md b/components/validation/index.md index b1d89a6f9b..ee751d8787 100644 --- a/components/validation/index.md +++ b/components/validation/index.md @@ -10,12 +10,63 @@ ## 何时使用 -需要对表单域进行校验时。 +同表单结合使用,对表单域进行校验。 ## API -属性如下 +```html + + + + + + +``` + + +### Validation + +| 属性 | 类型 | 说明 | +|-----------|---------------|--------------------| +| onValidate | func | 当内部 Validator 开始校验时被调用。 | + +| 方法 | 说明 | +|------------|----------------| +| validate(callback) | 对所有表单域进行校验。 | +| reset() | 将表单域的值恢复为初始值。 | +| forceValidate(fields, callback) | 对指定的表单域进行校验,fields 对应每个 Validator 包裹的表单域的 name 属性值。| + +### Validation.Validator + +一个 Validator 对应一个表单域,校验的表单域需要声明 `name` 属性作为校验标识,如 ``。 + +| 属性 | 类型 | 默认值 | 说明 | +|-----------|---------------|--------------------| +| rules | array 或者 object | | 支持多规则校验,默认提供的规则详见 [async-validator](https://github.com/yiminghe/async-validator),同时支持用户自定义校验规则。| +| trigger | String | onChange | 设定如何触发校验动作。 | + +### rules 说明([async-validator](https://github.com/yiminghe/async-validator)) + +示例: `{type: "string", required: true, min: 5, message: "请至少填写 5 个字符。" }` + +- `type` : 声明校验的值类型(如 string email),这样就会使用默认提供的规则进行校验,更多详见 [type](https://github.com/yiminghe/async-validator#user-content-type); +- `required`: 是否必填; +- `pattern`: 声明校验正则表达式; +- `min` / `max`: 最小值、最大值声明; +- `len`: 字符长度; +- `enum`: 枚举值,对应 type 值为 `enum`,例如 `role: {type: "enum", enum: ['A', 'B', 'C']}`; +- `whitespace`: 是否允许空格, `true` 为允许; +- `fields`: 当你需要对类型为 object 或者 array 的每个值做校验时使用,详见 [fields](https://github.com/yiminghe/async-validator#deep-rules); +- `transform`: 当你需要在校验前对值做一些处理的时候; +- `message`: 自定义提示信息,[更多](https://github.com/yiminghe/async-validator#messages); +- `validator`: 自定义校验规则。 + + + + + + + -| 成员 | 说明 | 类型 | 默认值 | -|-------------|----------------|--------------------|--------------| From e74845896c1f369a89426c27d45885a647defb6a Mon Sep 17 00:00:00 2001 From: SimaQ Date: Fri, 31 Jul 2015 16:26:09 +0800 Subject: [PATCH 10/12] update form demo --- components/form/demo/horizontal-form.md | 4 ++-- components/form/demo/inputs.md | 5 +---- components/form/demo/validate.md | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/components/form/demo/horizontal-form.md b/components/form/demo/horizontal-form.md index fb9b5aafe6..5b3600c4e2 100644 --- a/components/form/demo/horizontal-form.md +++ b/components/form/demo/horizontal-form.md @@ -4,11 +4,11 @@ 为 `` 标签添加 `.ant-form-horizontal` 类(这让 `.ant-form-item` 表现为栅格系统中的 `row`),并结合使用我们提供的 [栅格系统](http://ant.design/components/layout/),可以实现 label 标签和表单控件的水平排列。 -如需将一行静态文本和 `
-
+
@@ -92,7 +90,6 @@ React.render(
-

请输入正确的电话号码

diff --git a/components/form/demo/validate.md b/components/form/demo/validate.md index 98ac20e4c0..c75443565c 100644 --- a/components/form/demo/validate.md +++ b/components/form/demo/validate.md @@ -8,7 +8,7 @@ 将以上三种校验状态类添加到这些控件的父级元素即可。 -另外为输入框添加反馈图标,可以更好地反馈当前的校验状态,使用 `.has-feedback` 类包裹 input 输入框即可。 +另外为输入框添加反馈图标,可以更好地反馈当前的校验状态,使用 `.has-feedback` 类包裹 input 输入框即可,在这里校验状态类就要和 `.has-feedback` 类同级。 **注意**: 反馈图标只能使用在文本输入框 `` 元素上。 From 19560e2d753e6f9bc3145d08a400c85e7828be8a Mon Sep 17 00:00:00 2001 From: SimaQ Date: Mon, 3 Aug 2015 11:32:45 +0800 Subject: [PATCH 11/12] update --- components/validation/index.md | 9 --------- package.json | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/components/validation/index.md b/components/validation/index.md index ee751d8787..9fe0001496 100644 --- a/components/validation/index.md +++ b/components/validation/index.md @@ -61,12 +61,3 @@ - `transform`: 当你需要在校验前对值做一些处理的时候; - `message`: 自定义提示信息,[更多](https://github.com/yiminghe/async-validator#messages); - `validator`: 自定义校验规则。 - - - - - - - - - diff --git a/package.json b/package.json index cbc2a2aaab..cc28db1d14 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "rc-collapse": "~1.2.3", "rc-dialog": "~4.4.0", "rc-dropdown": "~1.1.1", - "rc-form-validation": "^2.4.7", + "rc-form-validation": "~2.4.7", "rc-input-number": "~2.0.1", "rc-menu": "~3.4.2", "rc-notification": "~1.0.1", From 8516f308e55895926b59bfcb80f11afffdeb1fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=81=8F=E5=8F=B3?= Date: Mon, 3 Aug 2015 13:12:13 +0800 Subject: [PATCH 12/12] =?UTF-8?q?Revert=20"=E8=A1=A8=E5=8D=95=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/form/demo/horizontal-form.md | 4 +- components/form/demo/inputs.md | 5 +- components/form/demo/validate.md | 24 ++- components/validation/demo/basic.md | 240 ----------------------- components/validation/demo/customize.md | 249 ------------------------ components/validation/index.jsx | 3 - components/validation/index.md | 63 ------ index.js | 3 +- package.json | 1 - style/components/form.less | 73 ------- style/mixins/form.less | 6 +- 11 files changed, 25 insertions(+), 646 deletions(-) delete mode 100644 components/validation/demo/basic.md delete mode 100644 components/validation/demo/customize.md delete mode 100644 components/validation/index.jsx delete mode 100644 components/validation/index.md diff --git a/components/form/demo/horizontal-form.md b/components/form/demo/horizontal-form.md index 5b3600c4e2..fb9b5aafe6 100644 --- a/components/form/demo/horizontal-form.md +++ b/components/form/demo/horizontal-form.md @@ -4,11 +4,11 @@ 为 `
` 标签添加 `.ant-form-horizontal` 类(这让 `.ant-form-item` 表现为栅格系统中的 `row`),并结合使用我们提供的 [栅格系统](http://ant.design/components/layout/),可以实现 label 标签和表单控件的水平排列。 -如需将一行静态文本和 `
-
+
@@ -90,6 +92,7 @@ React.render(
+

请输入正确的电话号码

diff --git a/components/form/demo/validate.md b/components/form/demo/validate.md index c75443565c..26d760a7a1 100644 --- a/components/form/demo/validate.md +++ b/components/form/demo/validate.md @@ -8,7 +8,7 @@ 将以上三种校验状态类添加到这些控件的父级元素即可。 -另外为输入框添加反馈图标,可以更好地反馈当前的校验状态,使用 `.has-feedback` 类包裹 input 输入框即可,在这里校验状态类就要和 `.has-feedback` 类同级。 +另外为输入框添加反馈图标,可以更好地反馈当前的校验状态,使用 `.has-feedback` 类包裹 input 输入框即可。 **注意**: 反馈图标只能使用在文本输入框 `` 元素上。 @@ -39,37 +39,41 @@
-
+
-
信息审核中...
+
+
信息审核中...
-
+
-
+
+
-
+
-
+
-
请输入数字和字母组合
+
+
请输入数字和字母组合
-
+
-
+
+
diff --git a/components/validation/demo/basic.md b/components/validation/demo/basic.md deleted file mode 100644 index 271534b8d2..0000000000 --- a/components/validation/demo/basic.md +++ /dev/null @@ -1,240 +0,0 @@ -# 基本 - -- order: 0 - -基本的表单校验栗子。 - -每个表单域要声明 `name` 属性作为校验的标识,可通过其 `isValidating` `errors` 属性判断是否处于校验中、是否校验不通过状态,具体可参见 **用户名** 校验。 - -表单提交的时候,通过 Validation 的 validate 方法判断是否所有表单域校验通过(isValid 会作为回调函数的参数传入)。 - ---- - -````jsx -var Validation = antd.Validation; -var Validator = Validation.Validator; -var Select = antd.Select; -var Option = Select.Option; -var Radio = antd.Radio; -var RadioGroup = antd.RadioGroup; - -function cx(classNames) { - if (typeof classNames === 'object') { - return Object.keys(classNames).filter(function(className) { - return classNames[className]; - }).join(' '); - } else { - return Array.prototype.join.call(arguments, ' '); - } -} - -var Form = React.createClass({ - mixins: [Validation.FieldMixin], - - getInitialState() { - return { - status: { - email: {}, - name: {}, - select: {}, - radio: {}, - passwd: {}, - rePasswd: {}, - textarea: {} - }, - formData: { - email: undefined, - name: undefined, - select: undefined, - radio: undefined, - passwd: undefined, - rePasswd: undefined, - textarea: undefined - } - }; - }, - - validateStyle(item, hasFeedback=true) { - var formData = this.state.formData; - var status = this.state.status; - - var classes = cx({ - 'has-feedback': hasFeedback, - 'has-error': status[item].errors, - 'is-validating': status[item].isValidating, - 'has-success': formData[item] && !status[item].errors && !status[item].isValidating - }); - - return classes; - }, - - handleReset(e) { - this.refs.validation.reset(); - this.setState(this.getInitialState()); - e.preventDefault(); - }, - - handleSubmit(e) { - e.preventDefault(); - var validation = this.refs.validation; - validation.validate((valid) => { - if (!valid) { - console.log('error in form'); - return; - } else { - console.log('submit'); - } - console.log(this.state.formData); - }); - }, - - userExists(rule, value, callback) { - if (!value) { - callback(); - } else { - setTimeout(function () { - if (value === 'yiminghe') { - callback([new Error('抱歉,该用户名已被占用。')]); - } else { - callback(); - } - }, 1000); - } - }, - - checkPass(rule, value, callback) { - if (this.state.formData.passwd) { - this.refs.validation.forceValidate(['rePasswd']); - } - callback(); - }, - - checkPass2(rule, value, callback) { - if (value !== this.state.formData.passwd) { - callback('两次输入密码不一致!'); - } else { - callback(); - } - }, - - render() { - var formData = this.state.formData; - var status = this.state.status; - - return ( -
- -
- -
-
- - - - {status.name.isValidating ?
正在校验中...
: null} - {status.name.errors ?
{status.name.errors.join(',')}
: null} -
-
-
- -
- -
-
- - - - {status.email.errors ?
{status.email.errors.join(',')}
: null} -
-
-
- -
- -
-
- - - - {status.select.errors ?
{status.select.errors.join(',')}
: null} -
-
-
- -
- -
-
- - - - - - - {status.radio.errors ?
{status.radio.errors.join(',')}
: null} -
-
-
- -
- -
-
- - - - {status.passwd.errors ?
{status.passwd.errors.join(',')}
: null} -
-
-
- -
- -
-
- - - - {status.rePasswd.errors ?
{status.rePasswd.errors.join(', ')}
: null} -
-
-
- - -
- -
-
- - - - {status.textarea.errors ?
{status.textarea.errors.join(',')}
: null} -
-
-
- -
-
- -     - 重 置 -
-
-
-
- ); - } -}); - -React.render(
, document.getElementById('components-validation-demo-basic')); -```` diff --git a/components/validation/demo/customize.md b/components/validation/demo/customize.md deleted file mode 100644 index be71cf5ce7..0000000000 --- a/components/validation/demo/customize.md +++ /dev/null @@ -1,249 +0,0 @@ -# 自定义校验规则 - -- order: 1 - -密码校验实例。 - ---- - -````jsx -var Validation = antd.Validation; -var Validator = Validation.Validator; - -function cx(classNames) { - if (typeof classNames === 'object') { - return Object.keys(classNames).filter(function(className) { - return classNames[className]; - }).join(' '); - } else { - return Array.prototype.join.call(arguments, ' '); - } -} - -var Form = React.createClass({ - mixins: [Validation.FieldMixin], - - getInitialState() { - return { - status: { - pass: {}, - rePass: {} - }, - formData: { - pass: undefined, - rePass: undefined - }, - passBarShow: false, // 是否显示密码强度提示条 - rePassBarShow: false, - passStrength: 'L', // 密码强度 - rePassStrength: 'L' - }; - }, - - handleSubmit(e) { - e.preventDefault(); - var validation = this.refs.validation; - validation.validate((valid) => { - if (!valid) { - console.log('error in form'); - return; - } else { - console.log('submit'); - } - console.log(this.state.formData); - }); - }, - - handleReset(e) { - this.refs.validation.reset(); - this.setState(this.getInitialState()); - e.preventDefault(); - }, - - renderValidateStyle(item, hasFeedback=true) { - var formData = this.state.formData; - var status = this.state.status; - - var classes = cx({ - 'has-feedback': hasFeedback, - 'has-error': status[item].errors, - 'is-validating': status[item].isValidating, - 'has-success': formData[item] && !status[item].errors && !status[item].isValidating - }); - - return classes; - }, - - getPassStrenth(value, type) { - if (value) { - var strength; - // 密码强度的校验规则自定义,这里只是做个简单的示例 - if (value.length < 6) { - strength = 'L'; - } else if (value.length <= 9) { - strength = 'M'; - } else { - strength = 'H'; - } - type === 'pass' ? this.setState({ passBarShow: true, passStrength: strength }) : this.setState({ rePassBarShow: true, rePassStrength: strength }); - } else { - type === 'pass' ? this.setState({ passBarShow: false }) : this.setState({ rePassBarShow: false }); - } - }, - - checkPass(rule, value, callback) { - this.getPassStrenth(value, 'pass'); - - if (this.state.formData.pass) { - this.refs.validation.forceValidate(['rePass']); - } - - callback(); - }, - - checkPass2(rule, value, callback) { - this.getPassStrenth(value, 'rePass'); - - if (value && value !== this.state.formData.pass) { - callback('两次输入密码不一致!'); - } else { - callback(); - } - }, - - renderPassStrengthBar(type) { - var strength = type === 'pass' ? this.state.passStrength : this.state.rePassStrength; - var classSet = cx({ - 'ant-pwd-strength': true, - 'ant-pwd-strength-low': strength === 'L', - 'ant-pwd-strength-medium': strength === 'M', - 'ant-pwd-strength-high': strength === 'H' - }); - var level = { - L: '低', - M: '中', - H: '高' - }; - - return ( -
-
    -
  • -
  • -
  • - - {level[strength]} - -
-
- ); - }, - - render() { - var formData = this.state.formData; - var status = this.state.status; - - return ( - - - -
- -
-
- - - - {status.pass.errors ?
{status.pass.errors.join(',')}
: null} -
-
-
- {this.state.passBarShow ? this.renderPassStrengthBar('pass') : null} -
-
- -
- -
-
- - - - {status.rePass.errors ?
{status.rePass.errors.join(', ')}
: null} -
-
-
- {this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null} -
-
- -
-
- -     - 重 置 -
-
-
-
- ); - } -}); - -React.render(
, document.getElementById('components-validation-demo-customize')); -```` - - diff --git a/components/validation/index.jsx b/components/validation/index.jsx deleted file mode 100644 index 00589d9285..0000000000 --- a/components/validation/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import Validation from 'rc-form-validation'; - -export default Validation; diff --git a/components/validation/index.md b/components/validation/index.md deleted file mode 100644 index 9fe0001496..0000000000 --- a/components/validation/index.md +++ /dev/null @@ -1,63 +0,0 @@ -# Validation - -- category: Components -- chinese: 表单校验 -- order: 19 - ---- - -表单校验。 - -## 何时使用 - -同表单结合使用,对表单域进行校验。 - -## API - -```html - - - - - - -``` - - -### Validation - -| 属性 | 类型 | 说明 | -|-----------|---------------|--------------------| -| onValidate | func | 当内部 Validator 开始校验时被调用。 | - -| 方法 | 说明 | -|------------|----------------| -| validate(callback) | 对所有表单域进行校验。 | -| reset() | 将表单域的值恢复为初始值。 | -| forceValidate(fields, callback) | 对指定的表单域进行校验,fields 对应每个 Validator 包裹的表单域的 name 属性值。| - -### Validation.Validator - -一个 Validator 对应一个表单域,校验的表单域需要声明 `name` 属性作为校验标识,如 ``。 - -| 属性 | 类型 | 默认值 | 说明 | -|-----------|---------------|--------------------| -| rules | array 或者 object | | 支持多规则校验,默认提供的规则详见 [async-validator](https://github.com/yiminghe/async-validator),同时支持用户自定义校验规则。| -| trigger | String | onChange | 设定如何触发校验动作。 | - -### rules 说明([async-validator](https://github.com/yiminghe/async-validator)) - -示例: `{type: "string", required: true, min: 5, message: "请至少填写 5 个字符。" }` - -- `type` : 声明校验的值类型(如 string email),这样就会使用默认提供的规则进行校验,更多详见 [type](https://github.com/yiminghe/async-validator#user-content-type); -- `required`: 是否必填; -- `pattern`: 声明校验正则表达式; -- `min` / `max`: 最小值、最大值声明; -- `len`: 字符长度; -- `enum`: 枚举值,对应 type 值为 `enum`,例如 `role: {type: "enum", enum: ['A', 'B', 'C']}`; -- `whitespace`: 是否允许空格, `true` 为允许; -- `fields`: 当你需要对类型为 object 或者 array 的每个值做校验时使用,详见 [fields](https://github.com/yiminghe/async-validator#deep-rules); -- `transform`: 当你需要在校验前对值做一些处理的时候; -- `message`: 自定义提示信息,[更多](https://github.com/yiminghe/async-validator#messages); -- `validator`: 自定义校验规则。 diff --git a/index.js b/index.js index 28a70941f0..78dad62408 100644 --- a/index.js +++ b/index.js @@ -23,8 +23,7 @@ var antd = { message: require('./components/message'), Slider: require('./components/slider'), Radio: require('./components/radio'), - RadioGroup: require('./components/radio/group'), - Validation: require('./components/validation') + RadioGroup: require('./components/radio/group') }; module.exports = antd; diff --git a/package.json b/package.json index cc28db1d14..88e44618cf 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "rc-collapse": "~1.2.3", "rc-dialog": "~4.4.0", "rc-dropdown": "~1.1.1", - "rc-form-validation": "~2.4.7", "rc-input-number": "~2.0.1", "rc-menu": "~3.4.2", "rc-notification": "~1.0.1", diff --git a/style/components/form.less b/style/components/form.less index 2c08df511b..36e64e8fa4 100644 --- a/style/components/form.less +++ b/style/components/form.less @@ -260,88 +260,15 @@ form { } // Validation state -.has-success, .has-warning, .has-error, .is-validating { - &.has-feedback:after { - position: absolute; - bottom: 0; - right: 0; - font-family: "anticon" !important; - width: 32px; - height: 32px; - line-height: 32px; - text-align: center; - font-size: 14px; - } -} - .has-success { .form-control-validation(@success-color; @input-hover-border-color;); .@{css-prefix}input { border-color: @input-border-color; - box-shadow: none; - } - - &.has-feedback:after { - content: '\e614'; - color: @success-color; } } - .has-warning { .form-control-validation(@warning-color; @warning-color;); - - &.has-feedback:after { - content: '\e628'; - color: @warning-color; - } - - // ant-select - .@{selectPrefixCls} { - &-selection { - border-color: @warning-color; - box-shadow: 0 0 0 2px tint(@warning-color, 80%); - } - &-arrow { - color: @warning-color; - } - } - - // ant-datepicker - .@{prefixCalendarClass}-picker-icon:after { - color: @warning-color; - } } - .has-error { .form-control-validation(@error-color; @error-color;); - - &.has-feedback:after { - content: '\e628'; - color: @error-color; - } - - // ant-select - .@{selectPrefixCls} { - &-selection { - border-color: @error-color; - box-shadow: 0 0 0 2px tint(@error-color, 80%); - } - - &-arrow { - color: @error-color; - } - } - - // ant-datepicker - .@{prefixCalendarClass}-picker-icon:after { - color: @error-color; - } -} - -.is-validating { - &.has-feedback:after { - display: inline-block; - .animation(loadingCircle 1s infinite linear); - content:"\e610"; - } } diff --git a/style/mixins/form.less b/style/mixins/form.less index edd5b36c97..bf9ffddade 100644 --- a/style/mixins/form.less +++ b/style/mixins/form.less @@ -5,8 +5,10 @@ // 输入框的不同校验状态 .@{css-prefix}input { border-color: @border-color; - box-shadow: 0 0 0 2px tint(@border-color, 80%); - + &:focus { + border-color: @border-color; + box-shadow: 0 0 0 2px tint(@border-color, 80%); + } &:not([disabled]):hover { border-color: @border-color; }