Skip to content

Commit

Permalink
Merge pull request #1681 from XiaoMi/feature/#1680
Browse files Browse the repository at this point in the history
Feature/#1680
  • Loading branch information
GleanCoder1116 committed Apr 16, 2021
2 parents bba2d88 + 6a2e1a2 commit 41f1883
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 18 deletions.
66 changes: 63 additions & 3 deletions components/form/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,14 @@ const InternalForm = (props) => {
},
[fields, initialValues, onValuesChange, internalValuesChange, listNames, _Immutable]
)
// 对整个表单进行校验
const validate = useCallback(
(cb, validateNames) => {
/**
* 对整个表单进行校验
* @param {Function} cb callback: (fields: Object, errors: Object) => void
* @param {Array} validateNames fields:Array
* @param {Boolean} showError 静默检验,是否在界面中展示错误信息
*/
(cb, validateNames, showError = true) => {
const values = {}
let errors = {}
const fields = _.cloneDeep(_Immutable.current.currentStateFields())
Expand All @@ -169,7 +174,8 @@ const InternalForm = (props) => {
errors[field] = { errors: errorsMsg }
}
},
value
value,
showError
)
})
errors = Object.keys(errors).length === 0 ? null : errors
Expand All @@ -178,6 +184,58 @@ const InternalForm = (props) => {
},
[fields, _Immutable]
)
// 错误信息 不触发校验
const getFieldsError = useCallback(
(validateNames) => {
const errors = {}
const fields = _.cloneDeep(_Immutable.current.currentStateFields())
const _fields = fields.filter((fieldChild) => {
const { field } = fieldChild
return Array.isArray(validateNames) ? validateNames.includes(field) : true
})

_fields.forEach((fieldChild) => {
const { field, value } = fieldChild
// 对指定的字段进行校验 其他字段过滤不校验
fieldChild.validate(
'',
(error) => {
if (error) {
const errorsMsg = error.map((err) => {
return err.message
})
errors[field] = { errors: errorsMsg }
}
},
value,
false
)
})
return Object.keys(errors).length === 0 ? null : errors
},
[fields, _Immutable]
)
// 静态获取表单数据,不触发数据校验
const getFieldsValue = useCallback(
(validateNames) => {
const values = {}
const fields = _.cloneDeep(_Immutable.current.currentStateFields())
if (fields.length === 0) {
return
}
fields
.filter((fieldChild) => {
const { field } = fieldChild
return Array.isArray(validateNames) ? validateNames.includes(field) : true
})
.forEach((item) => {
const { field, value } = item
values[field] = value
})
return values
},
[fields, _Immutable]
)

const validateField = useCallback(
(key, cb) => {
Expand Down Expand Up @@ -209,6 +267,8 @@ const InternalForm = (props) => {
validateField,
validate,
setFieldsValue,
getFieldsValue,
getFieldsError,
updateFormSchema
}))
return (
Expand Down
41 changes: 30 additions & 11 deletions components/form/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,37 @@ const FormItem = (props) => {
let formRules = formProps.rules

formRules = formRules ? formRules[field] : []
return [].concat(selfRules || formRules || [])
// 修复 AsyncValidator 接收 jsx deepMerge 卡顿问题
// https://github.com/yiminghe/async-validator/blob/bc18bca1d9422911dc5ac3ce2a99599821534bd9/src/util.js#L220
const rulesArr = [].concat(selfRules || formRules || [])
rulesArr.forEach((item) => {
const { message } = item
if (React.isValidElement(message)) {
item.message = Object.assign({}, { ...message }, { _owner: null })
}
})
return rulesArr.concat()
}, [props, formProps, required, field])
// 过滤含有该trigger触发方式的rules
const getFilteredRule = useCallback((trigger) => {
const rules = getRules()
return rules.filter((rule) => {
return !rule.trigger || rule.trigger.indexOf(trigger) !== -1
})
})
const getFilteredRule = useCallback(
(trigger) => {
const rules = getRules()
return rules.filter((rule) => {
return !rule.trigger || rule.trigger.indexOf(trigger) !== -1
})
},
[getRules]
)
// 父级调用
const validate = useCallback(
(trigger, cb, currentValue) => {
/**
* 触发Item校验
* @param {Sting} trigger 触发事件 在什么情况下触发 'onChange, onBlur'
* @param {Function} cb 自定义消息提示回调方法
* @param {Any} currentValue 校验值
* @param {Boolean} showError 静默检验,是否在界面中展示错误信息
*/
(trigger, cb, currentValue, showError = true) => {
const triggerRules = getFilteredRule(trigger)
if (!triggerRules || triggerRules.length === 0) {
if (cb instanceof Function) {
Expand All @@ -128,6 +147,7 @@ const FormItem = (props) => {
return true
}
const rules = getRules()

const validator = new AsyncValidator({
[field]: rules
})
Expand All @@ -138,7 +158,7 @@ const FormItem = (props) => {
firstFields: true
},
(errors) => {
setError(errors ? errors[0].message : '')
showError && setError(errors ? errors[0].message : '')
setValidating(false)
if (cb instanceof Function) {
cb(errors)
Expand Down Expand Up @@ -178,7 +198,7 @@ const FormItem = (props) => {
})
}
return isRequired
})
}, [getRules])

// 对字段的操作
const handleField = (triggerType, currentValue) => {
Expand Down Expand Up @@ -304,7 +324,6 @@ const FormItem = (props) => {
obj['hi-form-item--required'] = isRequired() || required
const _labelWidth = labelWidth()
const contentWidth = formProps.labelPlacement === 'top' ? '100%' : `calc(100% - ${_labelWidth}px)`

return (
<div className={classNames('hi-form-item', className, obj)} style={style} key={field}>
{label || label === '' ? (
Expand Down
2 changes: 1 addition & 1 deletion components/hi-request/mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const InternalRequest = (type: HiRequestType | null, url: string | HiRequestOpti
const _url = isHTTPLink(urlOption) ? urlOption : baseURL ? baseURL + urlOption : urlOption
const _type = type || typeOption

const _options = Object.assign({}, options, { url: _url, responseType })
const _options = Object.assign({}, options, baseOptions, { url: _url, responseType })

switch (_type) {
case 'jsonp':
Expand Down
2 changes: 0 additions & 2 deletions docs/demo/form/section-check.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ const code = [
<Col span={12}>
<Form
ref={this.form}
model={form}
rules={this.state.rules}
labelWidth='80'
labelPlacement='right'>
Expand Down Expand Up @@ -218,7 +217,6 @@ const code = [
},
formData: '',
countDown: 60,
codeDisabled: false
}
this.form = React.createRef()
Expand Down
125 changes: 125 additions & 0 deletions docs/demo/form/section-static.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* eslint-disable */
import React from 'react'
import DocViewer from '../../../libs/doc-viewer'
import Form, { LegacyForm } from '../../../components/form'
import Input from '../../../components/input'
import Checkbox from '../../../components/checkbox'
import Button from '../../../components/button'
import Grid from '../../../components/grid'
import Radio from '../../../components/radio'
import Select from '../../../components/select'
const prefix = 'form-static'
const rightOptions = ['静默获取表单值', '静默校验表单']
const desc = ['静默获取表单值:不触发校验', '静默校验表单: 触发校验, 但是页面中不显示错误信息']
const code = [
{
code: `import React from 'react'
import { Form, Grid, Radio, Button, Input } from '@hi-ui/hiui'\n
class Demo extends React.Component {
constructor(props){
super(props)
this.form = React.createRef()
}
componentDidMount() {
this.form.current && this.form.current.setFieldsValue({ phone: '不合法手机号', name: '' })
}
render (){
const FormItem = Form.Item
const FormSubmit = Form.Submit
const FormReset = Form.Reset
return (
<Form labelPlacement='left' labelWidth={80} ref={this.form}>
<FormItem label='手机号' field="phone" rules={{
trigger:'onChange',
type:'number',
validator: (rule,value,callback) => {
const telReg = /^[1][3|4|5|6|7|8|9][0-9]{9}$/
if(!value){
callback("请输入手机号")
} else if (!telReg.test(value)){
callback("请输入正确的手机号")
} else {
callback()
}
},
}}>
<Input placeholder='请输入手机号' style={{ width: 200 }} />
</FormItem>
<FormItem required={true} label="姓名" field="name">
<Input placeholder="请输入" style={{ width: 200 }}/>
</FormItem>
<FormItem label="" >
<Button type='primary'
onClick={()=>{
const values = this.form.current.getFieldsValue()
console.log('获取表单值, 但是不触发校验方法',values)
}}
>获取表单值</Button>
</FormItem>
</Form>
)
}
}`,
opt: ['静默获取表单值']
},
{
code: `import React from 'react'
import { Form, Grid, Radio, Button, Input } from '@hi-ui/hiui'\n
class Demo extends React.Component {
constructor(props){
super(props)
this.form = React.createRef()
}
componentDidMount() {
this.form.current && this.form.current.setFieldsValue({ phone: '不合法手机号', name: '' })
}
render (){
const FormItem = Form.Item
const FormSubmit = Form.Submit
const FormReset = Form.Reset
return (
<Form labelPlacement='left' labelWidth={80} ref={this.form}>
<FormItem label='手机号' field="phone" rules={{
trigger:'onChange',
type:'number',
validator: (rule,value,callback) => {
const telReg = /^[1][3|4|5|6|7|8|9][0-9]{9}$/
if(!value){
callback("请输入手机号")
} else if (!telReg.test(value)){
callback("请输入正确的手机号")
} else {
callback()
}
},
}}>
<Input placeholder='请输入手机号' style={{ width: 200 }} />
</FormItem>
<FormItem required={true} label="姓名" field="name">
<Input placeholder="请输入" style={{ width: 200 }}/>
</FormItem>
<FormItem label="" >
<Button type='primary'
onClick={()=>{
const error = this.form.current.getFieldsError()
console.log('获取表单错误信息',error)
}}
>获取表单错误信息</Button>
</FormItem>
</Form>
)
}
}`,
opt: ['静默校验表单']
}
]
const DemoAlign = () => (
<DocViewer
code={code}
scope={{ Form, Input, Button, Checkbox, LegacyForm, Grid, Radio, Select }}
prefix={prefix}
desc={desc}
rightOptions={rightOptions}
/>
)
export default DemoAlign
10 changes: 9 additions & 1 deletion docs/zh-CN/components/form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ import DemoCheck from '../../demo/form/section-check.jsx'

<DemoCheck />

## 表单状态

import DemoStatic from '../../demo/form/section-static.jsx'

<DemoStatic />

## 字段嵌套

import DemoField from '../../demo/form/section-field.jsx'
Expand Down Expand Up @@ -150,9 +156,11 @@ import DemoUseForm from '../../demo/form/section-useForm.jsx'
| 方法名 | 说明 |
| -------------------------------------------------------------------------- | ------------------------------------------------------------ |
| validate(callback: (fields: Object, errors: Object) => void, fields:Array) | 对整个表单进行校验, 对应 [Form.Submit](#Form.Submit)中的 API |
| validateField(fields: string, callback: errors => void) | 对指定表单字段进行校验 |
| validateField(fields: []string, callback: errors => void) | 对指定表单字段进行校验 |
| resetValidates(callback:() => void, fields:Array, toDefault:boolean) | 重置整个表单的验证,对应 [Form.Reset](#Form.Reset)中的 API |
| setFieldsValue(field: Object) | 设置表单的值,在异步获取的数据回显的时候,使用该方法 |
| getFieldsValue( fields: Object ) => Object | 获取一组字段名对应的 Values 返回为数组形式, 不传入 fields;默认返回全部信息, 不会触发表单校验 |
| getFieldsError( fields: []string ) => Object | 获取一组字段名对应的错误信息,返回为数组形式, 不传入 fields;默认返回全部信息 |
| updateFormSchema( schemaData: {fileName: [SchemaItem](#SchemaItem) } ) => void |**SchemaItem** 中对应的 **schema** 数据更新时,请调用该方法更新 |

## rules
Expand Down

0 comments on commit 41f1883

Please sign in to comment.