zhujun24
9 years ago
11 changed files with 646 additions and 25 deletions
@ -0,0 +1,240 @@ |
|||
# 基本 |
|||
|
|||
- 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 ( |
|||
<form onSubmit={this.handleSubmit} className="ant-form-horizontal"> |
|||
<Validation ref='validation' onValidate={this.handleValidate}> |
|||
<div className="ant-form-item"> |
|||
<label className="col-6" for="name" required>用户名:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("name")}> |
|||
<Validator rules={[{required: true, min: 5, message: '用户名至少为 5 个字符'}, {validator: this.userExists}]}> |
|||
<input name='name' className="ant-input" value={formData.name} /> |
|||
</Validator> |
|||
{status.name.isValidating ? <div className="ant-form-explain">正在校验中...</div> : null} |
|||
{status.name.errors ? <div className="ant-form-explain">{status.name.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" for="email" required>邮箱:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("email")}> |
|||
<Validator rules={{type: 'email', message: '请填写正确的邮箱地址'}} trigger="onBlur"> |
|||
<input name='email' className="ant-input" value={formData.email} onChange={this.setField.bind(this, 'email')}/> |
|||
</Validator> |
|||
{status.email.errors ? <div className="ant-form-explain">{status.email.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>国籍:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("select", false)}> |
|||
<Validator rules={[{required: true, message: '请选择您的国籍'}]}> |
|||
<Select style={{width:200}} name="select" value={formData.select}> |
|||
<Option value="china">中国</Option> |
|||
<Option value="use">美国</Option> |
|||
<Option value="japan">日本</Option> |
|||
<Option value="korean">韩国</Option> |
|||
<Option value="Thailand">泰国</Option> |
|||
</Select> |
|||
</Validator> |
|||
{status.select.errors ? <div className="ant-form-explain">{status.select.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item ant-form-item-compact"> |
|||
<label className="col-6" required>性别:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("radio", false)}> |
|||
<Validator rules={[{required: true, message: '请选择您的性别'}]}> |
|||
<RadioGroup name="radio" value={formData.radio}> |
|||
<Radio value="male">男</Radio> |
|||
<Radio value="female">女</Radio> |
|||
</RadioGroup> |
|||
</Validator> |
|||
{status.radio.errors ? <div className="ant-form-explain">{status.radio.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>密码:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("passwd", false)}> |
|||
<Validator trigger="onBlur" rules={[{required: true, whitespace: true, message: '请填写密码'}, {validator: this.checkPass}]}> |
|||
<input name='passwd' className="ant-input" type="password" value={formData.passwd}/> |
|||
</Validator> |
|||
{status.passwd.errors ? <div className="ant-form-explain">{status.passwd.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>确认密码:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("rePasswd", false)}> |
|||
<Validator trigger="onBlur" rules={[{ |
|||
required: true, |
|||
whitespace: true, |
|||
message: '请再次输入密码' |
|||
}, {validator: this.checkPass2}]}> |
|||
<input name='rePasswd' className="ant-input" type="password" value={formData.rePasswd}/> |
|||
</Validator> |
|||
{status.rePasswd.errors ? <div className="ant-form-explain"> {status.rePasswd.errors.join(', ')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>备注:</label> |
|||
<div className="col-12"> |
|||
<div className= {this.validateStyle("textarea", false)}> |
|||
<Validator rules={[{required: true, message: '真的不打算写点什么吗?'}]}> |
|||
<textarea className="ant-input" name="textarea" placeholder="写点什么吧"></textarea> |
|||
</Validator> |
|||
{status.textarea.errors ? <div className="ant-form-explain">{status.textarea.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<div className="col-offset-6 col-12"> |
|||
<button type="submit" className="ant-btn ant-btn-primary">确 定</button> |
|||
|
|||
<a href='#' className="ant-btn" onClick={this.handleReset}>重 置</a> |
|||
</div> |
|||
</div> |
|||
</Validation> |
|||
</form> |
|||
); |
|||
} |
|||
}); |
|||
|
|||
React.render(<Form />, document.getElementById('components-validation-demo-basic')); |
|||
```` |
@ -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 ( |
|||
<div> |
|||
<ul className={classSet}> |
|||
<li className="ant-pwd-strength-item ant-pwd-strength-item-1"></li> |
|||
<li className="ant-pwd-strength-item ant-pwd-strength-item-2"></li> |
|||
<li className="ant-pwd-strength-item ant-pwd-strength-item-3"></li> |
|||
<span className="ant-form-text"> |
|||
{level[strength]} |
|||
</span> |
|||
</ul> |
|||
</div> |
|||
); |
|||
}, |
|||
|
|||
render() { |
|||
var formData = this.state.formData; |
|||
var status = this.state.status; |
|||
|
|||
return ( |
|||
<form onSubmit={this.handleSubmit} className="ant-form-horizontal"> |
|||
<Validation ref='validation' onValidate={this.handleValidate}> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>密码:</label> |
|||
<div className="col-10"> |
|||
<div className= {this.renderValidateStyle("pass", false)}> |
|||
<Validator rules={[{required: true, whitespace: true, message: '请填写密码'}, {validator: this.checkPass}]} trigger="onChange"> |
|||
<input name='pass' className="ant-input" type="password" value={formData.pass}/> |
|||
</Validator> |
|||
{status.pass.errors ? <div className="ant-form-explain">{status.pass.errors.join(',')}</div> : null} |
|||
</div> |
|||
</div> |
|||
<div className="col-6"> |
|||
{this.state.passBarShow ? this.renderPassStrengthBar('pass') : null} |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<label className="col-6" required>确认密码:</label> |
|||
<div className="col-10"> |
|||
<div className= {this.renderValidateStyle("rePass", false)}> |
|||
<Validator rules={[{ |
|||
required: true, |
|||
whitespace: true, |
|||
message: '请再次输入密码' |
|||
}, {validator: this.checkPass2}]}> |
|||
<input name='rePass' className="ant-input" type="password" value={formData.rePass}/> |
|||
</Validator> |
|||
{status.rePass.errors ? <div className="ant-form-explain"> {status.rePass.errors.join(', ')}</div> : null} |
|||
</div> |
|||
</div> |
|||
<div className="col-6"> |
|||
{this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null} |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="ant-form-item"> |
|||
<div className="col-offset-6 col-12"> |
|||
<button type="submit" className="ant-btn ant-btn-primary">确 定</button> |
|||
|
|||
<a href='#' className="ant-btn" onClick={this.handleReset}>重 置</a> |
|||
</div> |
|||
</div> |
|||
</Validation> |
|||
</form> |
|||
); |
|||
} |
|||
}); |
|||
|
|||
React.render(<Form />, document.getElementById('components-validation-demo-customize')); |
|||
```` |
|||
|
|||
<style> |
|||
.ant-pwd-strength { |
|||
display: inline-block; |
|||
margin-left: 8px; |
|||
line-height: 32px; |
|||
height: 32px; |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
.ant-pwd-strength-item { |
|||
float: left; |
|||
margin-right: 1px; |
|||
margin-top: 12px; |
|||
width: 19px; |
|||
height: 8px; |
|||
line-height: 8px; |
|||
list-style: none; |
|||
background-color: #f3f3f3; |
|||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); |
|||
-webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); |
|||
-moz-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); |
|||
} |
|||
|
|||
.ant-pwd-strength-item-1 { |
|||
border-top-left-radius: 6px; |
|||
border-bottom-left-radius: 6px; |
|||
} |
|||
|
|||
.ant-pwd-strength-item-2 { |
|||
width: 20px; |
|||
} |
|||
|
|||
.ant-pwd-strength-item-3 { |
|||
border-top-right-radius: 6px; |
|||
border-bottom-right-radius: 6px; |
|||
margin-right: 8px; |
|||
} |
|||
|
|||
.ant-pwd-strength-low .ant-pwd-strength-item-1, .ant-pwd-strength-medium .ant-pwd-strength-item-1, .ant-pwd-strength-high .ant-pwd-strength-item-1 { |
|||
background-color: #FAC450; |
|||
} |
|||
|
|||
.ant-pwd-strength-medium .ant-pwd-strength-item-2, .ant-pwd-strength-high .ant-pwd-strength-item-2 { |
|||
background-color: rgba(135, 208, 104, .6); |
|||
} |
|||
|
|||
.ant-pwd-strength-high .ant-pwd-strength-item-3 { |
|||
background-color: #87D068; |
|||
} |
|||
</style> |
@ -0,0 +1,3 @@ |
|||
import Validation from 'rc-form-validation'; |
|||
|
|||
export default Validation; |
@ -0,0 +1,63 @@ |
|||
# Validation |
|||
|
|||
- category: Components |
|||
- chinese: 表单校验 |
|||
- order: 19 |
|||
|
|||
--- |
|||
|
|||
表单校验。 |
|||
|
|||
## 何时使用 |
|||
|
|||
同表单结合使用,对表单域进行校验。 |
|||
|
|||
## API |
|||
|
|||
```html |
|||
<Validation> |
|||
<Validator> |
|||
<CustomComponent> |
|||
<input /> |
|||
</CustomComponent |
|||
</Validator> |
|||
</Validation> |
|||
``` |
|||
|
|||
|
|||
### Validation |
|||
|
|||
| 属性 | 类型 | 说明 | |
|||
|-----------|---------------|--------------------| |
|||
| onValidate | func | 当内部 Validator 开始校验时被调用。 | |
|||
|
|||
| 方法 | 说明 | |
|||
|------------|----------------| |
|||
| validate(callback) | 对所有表单域进行校验。 | |
|||
| reset() | 将表单域的值恢复为初始值。 | |
|||
| forceValidate(fields, callback) | 对指定的表单域进行校验,fields 对应每个 Validator 包裹的表单域的 name 属性值。| |
|||
|
|||
### Validation.Validator |
|||
|
|||
一个 Validator 对应一个表单域,校验的表单域需要声明 `name` 属性作为校验标识,如 `<input name="username" />`。 |
|||
|
|||
| 属性 | 类型 | 默认值 | 说明 | |
|||
|-----------|---------------|--------------------| |
|||
| 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`: 自定义校验规则。 |
Loading…
Reference in new issue