feat: update form

pull/802/head
wangxueliang 2019-04-20 14:19:13 +08:00
parent 300fc9a2bc
commit 3a2977a2ed
17 changed files with 55 additions and 43 deletions

View File

@ -8,6 +8,7 @@ import createFormField from '../vc-form/src/createFormField';
import FormItem from './FormItem'; import FormItem from './FormItem';
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants'; import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants';
import { initDefaultProps } from '../_util/props-util'; import { initDefaultProps } from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
export const FormCreateOption = { export const FormCreateOption = {
onFieldsChange: PropTypes.func, onFieldsChange: PropTypes.func,
@ -15,6 +16,7 @@ export const FormCreateOption = {
mapPropsToFields: PropTypes.func, mapPropsToFields: PropTypes.func,
validateMessages: PropTypes.any, validateMessages: PropTypes.any,
withRef: PropTypes.bool, withRef: PropTypes.bool,
name: PropTypes.string,
}; };
// function create // function create
@ -119,15 +121,11 @@ export const ValidationRule = {
const Form = { const Form = {
name: 'AForm', name: 'AForm',
props: initDefaultProps(FormProps, { props: initDefaultProps(FormProps, {
prefixCls: 'ant-form',
layout: 'horizontal', layout: 'horizontal',
hideRequiredMark: false, hideRequiredMark: false,
}), }),
Item: FormItem, Item: FormItem,
createFormField: createFormField, createFormField: createFormField,
create: (options = {}) => { create: (options = {}) => {
return createDOMForm({ return createDOMForm({
fieldNameProp: 'id', fieldNameProp: 'id',
@ -166,6 +164,9 @@ const Form = {
: () => {}, : () => {},
}; };
}, },
inject: {
configProvider: { default: () => ({}) },
},
watch: { watch: {
form() { form() {
this.$forceUpdate(); this.$forceUpdate();
@ -196,7 +197,7 @@ const Form = {
render() { render() {
const { const {
prefixCls, prefixCls: customizePrefixCls,
hideRequiredMark, hideRequiredMark,
layout, layout,
onSubmit, onSubmit,
@ -204,6 +205,8 @@ const Form = {
autoFormCreate, autoFormCreate,
options = {}, options = {},
} = this; } = this;
const getPrefixCls = this.configProvider.getPrefixCls || ConfigConsumerProps.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls);
const formClassName = classNames(prefixCls, { const formClassName = classNames(prefixCls, {
[`${prefixCls}-horizontal`]: layout === 'horizontal', [`${prefixCls}-horizontal`]: layout === 'horizontal',

View File

@ -18,6 +18,7 @@ import getTransitionProps from '../_util/getTransitionProps';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import { cloneElement, cloneVNodes } from '../_util/vnode'; import { cloneElement, cloneVNodes } from '../_util/vnode';
import Icon from '../icon'; import Icon from '../icon';
import { ConfigConsumerProps } from '../config-provider';
function noop() {} function noop() {}
export const FormItemProps = { export const FormItemProps = {
@ -65,6 +66,7 @@ export default {
FormProps: { default: () => ({}) }, FormProps: { default: () => ({}) },
decoratorFormProps: { default: () => ({}) }, decoratorFormProps: { default: () => ({}) },
collectFormItemContext: { default: () => noop }, collectFormItemContext: { default: () => noop },
configProvider: { default: () => ({}) },
}, },
data() { data() {
return { helpShow: false }; return { helpShow: false };
@ -197,8 +199,7 @@ export default {
} }
}, },
renderHelp() { renderHelp(prefixCls) {
const prefixCls = this.prefixCls;
const help = this.getHelpMessage(); const help = this.getHelpMessage();
const children = help ? ( const children = help ? (
<div class={`${prefixCls}-explain`} key="help"> <div class={`${prefixCls}-explain`} key="help">
@ -219,8 +220,7 @@ export default {
); );
}, },
renderExtra() { renderExtra(prefixCls) {
const { prefixCls } = this;
const extra = getComponentFromProp(this, 'extra'); const extra = getComponentFromProp(this, 'extra');
return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null; return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null;
}, },
@ -244,7 +244,7 @@ export default {
return ''; return '';
}, },
renderValidateWrapper(c1, c2, c3) { renderValidateWrapper(prefixCls, c1, c2, c3) {
const props = this.$props; const props = this.$props;
const onlyControl = this.getOnlyControl; const onlyControl = this.getOnlyControl;
const validateStatus = const validateStatus =
@ -252,9 +252,9 @@ export default {
? this.getValidateStatus() ? this.getValidateStatus()
: props.validateStatus; : props.validateStatus;
let classes = `${props.prefixCls}-item-control`; let classes = `${prefixCls}-item-control`;
if (validateStatus) { if (validateStatus) {
classes = classNames(`${props.prefixCls}-item-control`, { classes = classNames(`${prefixCls}-item-control`, {
'has-feedback': props.hasFeedback || validateStatus === 'validating', 'has-feedback': props.hasFeedback || validateStatus === 'validating',
'has-success': validateStatus === 'success', 'has-success': validateStatus === 'success',
'has-warning': validateStatus === 'warning', 'has-warning': validateStatus === 'warning',
@ -282,13 +282,13 @@ export default {
} }
const icon = const icon =
props.hasFeedback && iconType ? ( props.hasFeedback && iconType ? (
<span class={`${props.prefixCls}-item-children-icon`}> <span class={`${prefixCls}-item-children-icon`}>
<Icon type={iconType} theme={iconType === 'loading' ? 'outlined' : 'filled'} /> <Icon type={iconType} theme={iconType === 'loading' ? 'outlined' : 'filled'} />
</span> </span>
) : null; ) : null;
return ( return (
<div class={classes}> <div class={classes}>
<span class={`${props.prefixCls}-item-children`}> <span class={`${prefixCls}-item-children`}>
{c1} {c1}
{icon} {icon}
</span> </span>
@ -298,8 +298,8 @@ export default {
); );
}, },
renderWrapper(children) { renderWrapper(prefixCls, children) {
const { prefixCls, wrapperCol = {} } = this; const { wrapperCol = {} } = this;
const { class: cls, style, id, on, ...restProps } = wrapperCol; const { class: cls, style, id, on, ...restProps } = wrapperCol;
const className = classNames(`${prefixCls}-item-control-wrapper`, cls); const className = classNames(`${prefixCls}-item-control-wrapper`, cls);
const colProps = { const colProps = {
@ -353,8 +353,8 @@ export default {
} }
}, },
renderLabel() { renderLabel(prefixCls) {
const { prefixCls, labelCol = {}, colon, id } = this; const { labelCol = {}, colon, id } = this;
const label = getComponentFromProp(this, 'label'); const label = getComponentFromProp(this, 'label');
const required = this.isRequired(); const required = this.isRequired();
const { const {
@ -398,21 +398,29 @@ export default {
</Col> </Col>
) : null; ) : null;
}, },
renderChildren() { renderChildren(prefixCls) {
return [ return [
this.renderLabel(), this.renderLabel(prefixCls),
this.renderWrapper( this.renderWrapper(
this.renderValidateWrapper(this.slotDefault, this.renderHelp(), this.renderExtra()), prefixCls,
this.renderValidateWrapper(
prefixCls,
this.slotDefault,
this.renderHelp(prefixCls),
this.renderExtra(prefixCls)
),
), ),
]; ];
}, },
renderFormItem(children) { renderFormItem() {
const props = this.$props; const { prefixCls: customizePrefixCls, colon } = this.$props;
const prefixCls = props.prefixCls; const getPrefixCls = this.configProvider.getPrefixCls || ConfigConsumerProps.getPrefixCls;
const prefixCls = getPrefixCls('form', customizePrefixCls);
const children = this.renderChildren(prefixCls);
const itemClassName = { const itemClassName = {
[`${prefixCls}-item`]: true, [`${prefixCls}-item`]: true,
[`${prefixCls}-item-with-help`]: this.helpShow, [`${prefixCls}-item-with-help`]: this.helpShow,
[`${prefixCls}-item-no-colon`]: !props.colon, [`${prefixCls}-item-no-colon`]: !colon,
}; };
return <Row class={classNames(itemClassName)}>{children}</Row>; return <Row class={classNames(itemClassName)}>{children}</Row>;
@ -478,8 +486,6 @@ export default {
} else { } else {
this.slotDefault = child; this.slotDefault = child;
} }
return this.renderFormItem();
const children = this.renderChildren();
return this.renderFormItem(children);
}, },
}; };

View File

@ -77,7 +77,7 @@ export default {
data () { data () {
return { return {
expand: false, expand: false,
form: this.$form.createForm(this), form: this.$form.createForm(this, { name: 'advanced_search' }),
}; };
}, },
computed: { computed: {

View File

@ -65,7 +65,7 @@ export default {
data () { data () {
return { return {
formLayout: 'horizontal', formLayout: 'horizontal',
form: this.$form.createForm(this), form: this.$form.createForm(this, { name: 'coordinated' }),
}; };
}, },
methods: { methods: {

View File

@ -111,7 +111,7 @@ export default {
PriceInput, PriceInput,
}, },
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'customized_form_controls' });
}, },
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {

View File

@ -89,7 +89,7 @@ export default {
}; };
}, },
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'dynamic_form_item' });
this.form.getFieldDecorator('keys', { initialValue: [], preserve: true }); this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
}, },
methods: { methods: {
@ -112,7 +112,7 @@ export default {
const { form } = this; const { form } = this;
// can use data-binding to get // can use data-binding to get
const keys = form.getFieldValue('keys'); const keys = form.getFieldValue('keys');
const nextKeys = keys.concat(++id); const nextKeys = keys.concat(id++);
// can use data-binding to set // can use data-binding to set
// important! notify form to detect changes // important! notify form to detect changes
form.setFieldsValue({ form.setFieldsValue({

View File

@ -77,7 +77,7 @@ export default {
checkNick: false, checkNick: false,
formItemLayout, formItemLayout,
formTailLayout, formTailLayout,
form: this.$form.createForm(this), form: this.$form.createForm(this, { name: 'dynamic_rule' }),
}; };
}, },
methods: { methods: {

View File

@ -29,7 +29,7 @@ When user visit a page with a list of items, and want to create a new item. The
const CollectionCreateForm = { const CollectionCreateForm = {
props: ['visible'], props: ['visible'],
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'form_in_modal' });
}, },
template: ` template: `
<a-modal <a-modal

View File

@ -48,6 +48,7 @@ const CustomizedForm = {
`, `,
created () { created () {
this.form = this.$form.createForm(this, { this.form = this.$form.createForm(this, {
name: 'global_state',
onFieldsChange: (_, changedFields) => { onFieldsChange: (_, changedFields) => {
this.$emit('change', changedFields); this.$emit('change', changedFields);
}, },

View File

@ -72,7 +72,7 @@ export default {
data () { data () {
return { return {
hasErrors, hasErrors,
form: this.$form.createForm(this), form: this.$form.createForm(this, { name: 'horizontal_login' }),
}; };
}, },
mounted () { mounted () {

View File

@ -82,7 +82,7 @@ Normal login form which can contain more elements.
export default { export default {
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'normal_login' });
}, },
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {

View File

@ -248,7 +248,7 @@ export default {
}; };
}, },
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'register' });
}, },
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {

View File

@ -95,7 +95,7 @@ export default {
}; };
}, },
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'time_related_controls' });
}, },
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {

View File

@ -5,7 +5,7 @@
<us> <us>
#### Other Form Controls #### Other Form Controls
Demostration for validataion configuration for form controls which are not show in the above demos. Demonstration of validation configuration for form controls which are not shown in the demos above.
</us> </us>
@ -252,7 +252,7 @@ export default {
}, },
}), }),
beforeCreate () { beforeCreate () {
this.form = this.$form.createForm(this); this.form = this.$form.createForm(this, { name: 'validate_other' });
}, },
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {

View File

@ -77,7 +77,7 @@ We provide properties like `validateStatus` `help` `hasFeedback` to customize yo
validate-status="warning" validate-status="warning"
> >
<a-input <a-input
id="warning" id="warning2"
placeholder="Warning" placeholder="Warning"
/> />
</a-form-item> </a-form-item>
@ -91,7 +91,7 @@ We provide properties like `validateStatus` `help` `hasFeedback` to customize yo
help="Should be combination of numbers & alphabets" help="Should be combination of numbers & alphabets"
> >
<a-input <a-input
id="error" id="error2"
placeholder="unavailable choice" placeholder="unavailable choice"
/> />
</a-form-item> </a-form-item>

View File

@ -48,6 +48,7 @@ The following `options` are available:
| -------- | ----------- | ---- | | -------- | ----------- | ---- |
| props | Only supports the use of Form.create({})(CustomizedForm). declare props on form(和[like vue props]( https://vuejs.org/v2/api/#props)) | {} | | props | Only supports the use of Form.create({})(CustomizedForm). declare props on form(和[like vue props]( https://vuejs.org/v2/api/#props)) | {} |
| mapPropsToFields | Convert props to field value(e.g. reading the values from Redux store). And you must mark returned fields with [`Form.createFormField`](#Form.createFormField). If you use `$form.createForm` to create a collector, you can map any data to the Field without being bound by the parent component. | (props) => Object{ fieldName: FormField { value } } | | mapPropsToFields | Convert props to field value(e.g. reading the values from Redux store). And you must mark returned fields with [`Form.createFormField`](#Form.createFormField). If you use `$form.createForm` to create a collector, you can map any data to the Field without being bound by the parent component. | (props) => Object{ fieldName: FormField { value } } |
| name | Set the id prefix of fields under form | - |
| validateMessages | Default validate message. And its format is similar with [newMessages](https://github.com/yiminghe/async-validator/blob/master/src/messages.js)'s returned value | Object { [nested.path]&#x3A; String } | | validateMessages | Default validate message. And its format is similar with [newMessages](https://github.com/yiminghe/async-validator/blob/master/src/messages.js)'s returned value | Object { [nested.path]&#x3A; String } |
| onFieldsChange | Specify a function that will be called when the value a `Form.Item` gets changed. Usage example: saving the field's value to Redux store. | Function(props, fields) | | onFieldsChange | Specify a function that will be called when the value a `Form.Item` gets changed. Usage example: saving the field's value to Redux store. | Function(props, fields) |
| onValuesChange | A handler while value of any field is changed | (props, values) => void | | onValuesChange | A handler while value of any field is changed | (props, values) => void |

View File

@ -46,6 +46,7 @@ export default {
| --- | --- | --- | | --- | --- | --- |
| props | 仅仅支持Form.create({})(CustomizedForm)的使用方式,父组件需要映射到表单项上的属性声明(和[vue组件props一致]( https://vuejs.org/v2/api/#props)) | {} | | props | 仅仅支持Form.create({})(CustomizedForm)的使用方式,父组件需要映射到表单项上的属性声明(和[vue组件props一致]( https://vuejs.org/v2/api/#props)) | {} |
| mapPropsToFields | 把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 [`Form.createFormField`](#Form.createFormField) 标记,如果使用$form.createForm创建收集器你可以将任何数据映射到Field中不受父组件约束 | (props) => ({ \[fieldName\]: FormField { value } }) | | mapPropsToFields | 把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 [`Form.createFormField`](#Form.createFormField) 标记,如果使用$form.createForm创建收集器你可以将任何数据映射到Field中不受父组件约束 | (props) => ({ \[fieldName\]: FormField { value } }) |
| name | 设置表单域内字段 id 的前缀 | - |
| validateMessages | 默认校验信息,可用于把默认错误信息改为中文等,格式与 [newMessages](https://github.com/yiminghe/async-validator/blob/master/src/messages.js) 返回值一致 | Object { [nested.path]&#x3A; String } | | validateMessages | 默认校验信息,可用于把默认错误信息改为中文等,格式与 [newMessages](https://github.com/yiminghe/async-validator/blob/master/src/messages.js) 返回值一致 | Object { [nested.path]&#x3A; String } |
| onFieldsChange | 当 `Form.Item` 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) | | onFieldsChange | 当 `Form.Item` 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) |
| onValuesChange | 任一表单域的值发生改变时的回调 | (props, values) => void | | onValuesChange | 任一表单域的值发生改变时的回调 | (props, values) => void |