Merge branch 'master' into feat-3.10.3

# Conflicts:
#	components/form/__tests__/__snapshots__/demo.test.js.snap
#	components/timeline/__tests__/__snapshots__/demo.test.js.snap
pull/309/head
tangjinzhou 2018-12-08 09:35:55 +08:00
commit 7aa46bbdf8
37 changed files with 1279 additions and 981 deletions

View File

@ -12,6 +12,7 @@
"comma-dangle": [2, "always-multiline"],
"no-var": "error",
"no-unused-vars": "warn",
"camelcase": "off"
"camelcase": "off",
"no-extra-boolean-cast": "off"
}
}
}

View File

@ -2,6 +2,33 @@
---
## 1.1.10
`2018-12-7`
- 🔥🔥🔥 In the 1.1.10 version, the `Form` component better supports the single-file tempalte syntax. In previous versions, complex component requirements were required to be implemented using JSX. In order to better use the automatic collection and validation of Form forms in the template, we have optimized the way components are used. All Demo files are refactored using the latest syntax.
However, for the previous API, continue to support, you can not worry about the API changes, resulting in problems in the existing system.
````html
<template>
<a-form :form="form">
<a-form-item>
<a-input v-decorator="[id, options]">
</a-form-item>
</a-form>
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this, options)
},
}
</script>
````
- 🐞 Fix `Steps` component `labelPlacement` does not work [#281](https://github.com/vueComponent/ant-design-vue/issues/281)
- 🐞 Fix the `Timeline` component style problem, add `reverse` `mode` props [#8e37cd](https://github.com/vueComponent/ant-design-vue/commit/8e37cd89f92ee2541f641fd860785cfd2361b2b3)
- `Tree`
- 🐞 Fix `treeDefaultExpandedKeys` does not work [#284](https://github.com/vueComponent/ant-design-vue/issues/284)
- 🐞 Fixes the component not update when other array attributes such as `expandedKeys` `selectedKeys` changed by arrays mutation methods. [#239](https://github.com/vueComponent/ant-design-vue/issues/239)
## 1.1.9
`2018-11-26`

View File

@ -1,5 +1,35 @@
# 更新日志
---
## 1.1.10
`2018-12-7`
- 🔥🔥🔥 在1.1.10版本中`Form`组件更好地支持单文件tempalte语法在以往版本中对于复杂的组件需求需要使用JSX才可以实现。为了更好地在template中使用Form表单的自动收集校验功能我们优化了组件的使用方式。文档全部Demo使用最新语法重构。
不过对于以往API还是继续支持你可以不用担心API的改变导致已有系统出现问题。
````html
<template>
<a-form :form="form">
<a-form-item>
<a-input v-decorator="[id, options]">
</a-form-item>
</a-form>
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this, options)
},
}
</script>
````
- 🐞 修复`Steps`组件`labelPlacement`不生效问题 [#281](https://github.com/vueComponent/ant-design-vue/issues/281)
- 🐞 修复`Timeline`组件样式问题,添加`reverse` `mode`属性 [#8e37cd](https://github.com/vueComponent/ant-design-vue/commit/8e37cd89f92ee2541f641fd860785cfd2361b2b3)
- `Tree`
- 🐞 修复`treeDefaultExpandedKeys`不生效问题 [#284](https://github.com/vueComponent/ant-design-vue/issues/284)
- 🐞 修复`expandedKeys` `selectedKeys`等其它数组属性通过组件变异方法改变时组件不更新问题 [#239](https://github.com/vueComponent/ant-design-vue/issues/239)
---
## 1.1.9

View File

@ -0,0 +1,7 @@
export default {
// just for tag
install: (Vue, options) => {
Vue.directive('decorator', {
})
},
}

View File

@ -49,7 +49,7 @@ const getSlots = (ele) => {
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions || {}
}
const children = componentOptions.children || []
const children = ele.children || componentOptions.children || []
const slots = {}
children.forEach(child => {
const name = (child.data && child.data.slot) || 'default'
@ -58,6 +58,13 @@ const getSlots = (ele) => {
})
return slots
}
const getAllChildren = (ele) => {
let componentOptions = ele.componentOptions || {}
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions || {}
}
return ele.children || componentOptions.children || []
}
const getSlotOptions = (ele) => {
if (ele.fnOptions) { // 函数式组件
return ele.fnOptions
@ -258,5 +265,6 @@ export {
camelize,
getSlots,
getAllProps,
getAllChildren,
}
export default hasProp

View File

@ -1,6 +1,8 @@
import PropTypes from '../_util/vue-types'
import classNames from 'classnames'
import Vue from 'vue'
import isRegExp from 'lodash/isRegExp'
import warning from '../_util/warning'
import createDOMForm from '../vc-form/src/createDOMForm'
import createFormField from '../vc-form/src/createFormField'
import FormItem from './FormItem'
@ -54,7 +56,7 @@ export const WrappedFormUtils = {
export const FormProps = {
layout: PropTypes.oneOf(['horizontal', 'inline', 'vertical']),
form: PropTypes.shape(WrappedFormUtils).loose,
form: PropTypes.object,
// onSubmit: React.FormEventHandler<any>;
prefixCls: PropTypes.string,
hideRequiredMark: PropTypes.bool,
@ -110,29 +112,13 @@ export const ValidationRule = {
// validateFirst?: boolean;
// };
export default {
const Form = {
name: 'AForm',
props: initDefaultProps(FormProps, {
prefixCls: 'ant-form',
layout: 'horizontal',
hideRequiredMark: false,
}),
// static defaultProps = {
// prefixCls: 'ant-form',
// layout: 'horizontal',
// hideRequiredMark: false,
// onSubmit (e) {
// e.preventDefault()
// },
// };
// static propTypes = {
// prefixCls: PropTypes.string,
// layout: PropTypes.oneOf(['horizontal', 'inline', 'vertical']),
// children: PropTypes.any,
// onSubmit: PropTypes.func,
// hideRequiredMark: PropTypes.bool,
// };
Item: FormItem,
@ -146,11 +132,19 @@ export default {
fieldDataProp: FIELD_DATA_PROP,
})
},
createForm (context, options = {}) {
return new Vue(Form.create({ ...options, templateContext: context })())
},
provide () {
return {
FormProps: this.$props,
}
},
watch: {
form () {
this.$forceUpdate()
},
},
methods: {
onSubmit (e) {
const { $listeners } = this
@ -174,6 +168,10 @@ export default {
[`${prefixCls}-hide-required-mark`]: hideRequiredMark,
})
if (autoFormCreate) {
warning(
false,
'`autoFormCreate` is deprecated. please use `form` instead.'
)
const DomForm = this.DomForm || createDOMForm({
fieldNameProp: 'id',
...options,
@ -214,3 +212,5 @@ export default {
return <form onSubmit={onSubmit} class={formClassName}>{$slots.default}</form>
},
}
export default Form

View File

@ -1,14 +1,15 @@
import intersperse from 'intersperse'
import PropTypes from '../_util/vue-types'
import classNames from 'classnames'
import find from 'lodash/find'
import Row from '../grid/Row'
import Col, { ColProps } from '../grid/Col'
import warning from '../_util/warning'
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants'
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, getSlots, isValidElement } from '../_util/props-util'
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, isValidElement, getSlots, getAllChildren } from '../_util/props-util'
import getTransitionProps from '../_util/getTransitionProps'
import BaseMixin from '../_util/BaseMixin'
import { cloneElement } from '../_util/vnode'
import { cloneElement, cloneVNodes } from '../_util/vnode'
export const FormItemProps = {
id: PropTypes.string,
prefixCls: PropTypes.string,
@ -47,6 +48,10 @@ export default {
'`Form.Item` cannot generate `validateStatus` and `help` automatically, ' +
'while there are more than one `getFieldDecorator` in it.',
)
warning(
!this.fieldDecoratorId,
'`fieldDecoratorId` is deprecated. please use `v-decorator={id, options}` instead.'
)
},
methods: {
getHelpMessage () {
@ -81,15 +86,12 @@ export default {
if (getSlotOptions(child).__ANT_FORM_ITEM) {
continue
}
const attrs = child.data && child.data.attrs
if (!attrs) {
continue
}
const slots = getSlots(child)
const children = getAllChildren(child)
const attrs = child.data && child.data.attrs || {}
if (FIELD_META_PROP in attrs) { // And means FIELD_DATA_PROP in child.props, too.
controls.push(child)
} else if (slots.default) {
controls = controls.concat(this.getControls(slots.default, recursively))
} else if (children) {
controls = controls.concat(this.getControls(children, recursively))
}
}
return controls
@ -339,11 +341,39 @@ export default {
</Row>
)
},
decoratorOption (vnode) {
if (vnode.data && vnode.data.directives) {
const directive = find(vnode.data.directives, ['name', 'decorator'])
warning(
!directive || (directive && Array.isArray(directive.value)),
`Invalid directive: type check failed for directive "decorator". Expected Array, got ${typeof directive.value}. At ${vnode.tag}.`,
)
return directive ? directive.value : null
} else {
return null
}
},
decoratorChildren (vnodes) {
const { FormProps } = this
const getFieldDecorator = FormProps.form.getFieldDecorator
vnodes.forEach((vnode, index) => {
if (vnode.children) {
vnode.children = this.decoratorChildren(cloneVNodes(vnode.children))
} else if (vnode.componentOptions && vnode.componentOptions.children) {
vnode.componentOptions.children = this.decoratorChildren(cloneVNodes(vnode.componentOptions.children))
}
const option = this.decoratorOption(vnode)
if (option && option[0]) {
vnodes[index] = getFieldDecorator(option[0], option[1])(vnode)
}
})
return vnodes
},
},
render () {
const { $slots, decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}} = this
const child = filterEmpty($slots.default || [])
const { $slots, decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}, FormProps } = this
let child = filterEmpty($slots.default || [])
if (decoratorFormProps.form && fieldDecoratorId && child.length) {
const getFieldDecorator = decoratorFormProps.form.getFieldDecorator
child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0])
@ -351,9 +381,14 @@ export default {
!(child.length > 1),
'`autoFormCreate` just `decorator` then first children. but you can use JSX to support multiple children',
)
this.slotDefault = child
} else if (FormProps.form) {
child = cloneVNodes(child)
this.slotDefault = this.decoratorChildren(child)
} else {
this.slotDefault = child
}
this.slotDefault = child
const children = this.renderChildren()
return this.renderFormItem(children)
},

View File

@ -2,18 +2,8 @@
exports[`renders ./components/form/demo/advanced-search.vue correctly 1`] = `
<div id="components-form-demo-advanced-search">
<form class="ant-form ant-form-horizontal ant-advanced-search-form">
<form class="ant-advanced-search-form ant-form ant-form-horizontal">
<div class="ant-row" style="margin-left: -12px; margin-right: -12px;">
<div class="ant-col-8" style="display: block; padding-left: 12px; padding-right: 12px;">
<div class="ant-row ant-form-item">
<div class="ant-form-item-label"><label for="field-0" title="Field 0" class="ant-form-item-required">Field 0</label></div>
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><input placeholder="placeholder" type="text" data-__meta="[object Object]" data-__field="[object Object]" id="field-0" class="ant-input"></span>
<!---->
</div>
</div>
</div>
</div>
<div class="ant-col-8" style="display: block; padding-left: 12px; padding-right: 12px;">
<div class="ant-row ant-form-item">
<div class="ant-form-item-label"><label for="field-1" title="Field 1" class="ant-form-item-required">Field 1</label></div>
@ -64,7 +54,7 @@ exports[`renders ./components/form/demo/advanced-search.vue correctly 1`] = `
</div>
</div>
</div>
<div class="ant-col-8" style="display: none; padding-left: 12px; padding-right: 12px;">
<div class="ant-col-8" style="display: block; padding-left: 12px; padding-right: 12px;">
<div class="ant-row ant-form-item">
<div class="ant-form-item-label"><label for="field-6" title="Field 6" class="ant-form-item-required">Field 6</label></div>
<div class="ant-form-item-control-wrapper">
@ -104,11 +94,20 @@ exports[`renders ./components/form/demo/advanced-search.vue correctly 1`] = `
</div>
</div>
</div>
<div class="ant-col-8" style="display: none; padding-left: 12px; padding-right: 12px;">
<div class="ant-row ant-form-item">
<div class="ant-form-item-label"><label for="field-10" title="Field 10" class="ant-form-item-required">Field 10</label></div>
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><input placeholder="placeholder" type="text" data-__meta="[object Object]" data-__field="[object Object]" id="field-10" class="ant-input"></span>
<!---->
</div>
</div>
</div>
</div>
</div>
<div class="ant-row">
<div class="ant-col-24" style="text-align: right;"><button type="submit" class="ant-btn ant-btn-primary"><span>Search</span></button><button type="button" class="ant-btn ant-btn-default" style="margin-left: 8px;"><span>Clear</span></button><a style="margin-left: 8px; font-size: 12px;">Collapse <i class="anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" class="">
<path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path>
</svg></i></a></div>
<div class="ant-col-24" style="text-align: right;"><button type="submit" class="ant-btn ant-btn-primary"><span>Search</span></button> <button type="button" class="ant-btn ant-btn-default" style="margin-left: 8px;"><span>Clear</span></button> <a style="margin-left: 8px; font-size: 12px;">
Collapse <i class="anticon anticon-down"></i></a></div>
</div>
</form>
<div class="search-result-list">Search Result List</div>
@ -223,26 +222,18 @@ exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = `
exports[`renders ./components/form/demo/form-in-modal.vue correctly 1`] = `
<div><button type="button" class="ant-btn ant-btn-primary"><span>New Collection</span></button>
<!---->
<!--function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }-->
</div>
`;
exports[`renders ./components/form/demo/global-state.vue correctly 1`] = `
<div id="components-form-demo-global-state">
<form class="ant-form ant-form-inline">
<div class="ant-row ant-form-item">
<div class="ant-form-item-label"><label for="username" title="Username" class="ant-form-item-required">Username</label></div>
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control has-success"><span class="ant-form-item-children"><input value="benjycui" type="text" data-__meta="[object Object]" data-__field="[object Object]" id="username" class="ant-input"></span>
<!---->
</div>
</div>
</div>
</form><pre class="language-bash">{
<!--function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }--> <pre class="language-bash"> {
"username": {
"value": "benjycui"
}
}</pre>
}
</pre>
</div>
`;
@ -311,7 +302,7 @@ exports[`renders ./components/form/demo/layout.vue correctly 1`] = `
`;
exports[`renders ./components/form/demo/normal-login.vue correctly 1`] = `
<form class="ant-form ant-form-horizontal login-form" id="components-form-demo-normal-login">
<form class="login-form ant-form ant-form-horizontal" id="components-form-demo-normal-login">
<div class="ant-row ant-form-item">
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><span class="ant-input-affix-wrapper"><span class="ant-input-prefix"><i class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><input placeholder="Username" type="text" data-__meta="[object Object]" data-__field="[object Object]" id="userName" class="ant-input"></span></span>
@ -328,7 +319,10 @@ exports[`renders ./components/form/demo/normal-login.vue correctly 1`] = `
</div>
<div class="ant-row ant-form-item">
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control has-success"><span class="ant-form-item-children"><label class="ant-checkbox-wrapper"><span class="ant-checkbox ant-checkbox-checked"><input id="remember" type="checkbox" class="ant-checkbox-input" value=""><span class="ant-checkbox-inner"></span></span><span>Remember me</span></label><a href="" class="login-form-forgot">Forgot password</a><button type="submit" class="ant-btn ant-btn-primary login-form-button"><span>Log in</span></button>Or <a href="">register now!</a></span>
<div class="ant-form-item-control has-success"><span class="ant-form-item-children"><label class="ant-checkbox-wrapper"><span class="ant-checkbox ant-checkbox-checked"><input id="remember" type="checkbox" class="ant-checkbox-input" value=""><span class="ant-checkbox-inner"></span></span><span>
Remember me
</span></label><a href="" class="login-form-forgot">Forgot password</a><button type="submit" class="login-form-button ant-btn ant-btn-primary"><span>Log in</span></button>
Or <a href="">register now!</a></span>
<!---->
</div>
</div>
@ -585,7 +579,7 @@ exports[`renders ./components/form/demo/validate-other.vue correctly 1`] = `
</div>
</div>
<div class="ant-row ant-form-item">
<div class="ant-col-6 ant-form-item-label"><label title="Dragger" class="">Dragger</label></div>
<div class="ant-col-6 ant-form-item-label"><label for="dragger" title="Dragger" class="">Dragger</label></div>
<div class="ant-col-14 ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><div class="dropbox"><span data-__meta="[object Object]" data-__field="[object Object]" id="dragger" class=""><div class="ant-upload ant-upload-drag"><!----></div><div class="ant-upload-list ant-upload-list-text"></div></span></div></span>
<!---->

View File

@ -10,16 +10,60 @@ Three columns layout is often used for advanced searching of data table.
Because the width of label is not fixed, you may need to adjust it by customizing its style.
</us>
<template>
<div id='components-form-demo-advanced-search'>
<a-form
class='ant-advanced-search-form'
@submit="handleSearch"
:form="form"
>
<a-row :gutter="24">
<a-col v-for="i in 10" :span="8" :key="i" :style="{ display: i < count ? 'block' : 'none' }">
<a-form-item :label="`Field ${i}`">
<a-input
v-decorator="[
`field-${i}`,
{
rules: [{
required: true,
message: 'Input something!',
}],
}
]"
placeholder='placeholder'
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24" :style="{ textAlign: 'right' }">
<a-button type='primary' htmlType='submit'>Search</a-button>
<a-button :style="{ marginLeft: '8px' }" @click="handleReset">
Clear
</a-button>
<a :style="{ marginLeft: '8px', fontSize: '12px' }" @click="toggle">
Collapse <a-icon :type="expand ? 'up' : 'down'" />
</a>
</a-col>
</a-row>
</a-form>
<div class='search-result-list'>Search Result List</div>
</div>
</template>
<script>
import { Form } from 'ant-design-vue'
const AdvancedSearchForm = {
export default {
data () {
return {
expand: false,
form: this.$form.createForm(this),
}
},
computed: {
count () {
return this.expand ? 11 : 7
},
},
methods: {
handleSearch (e) {
e.preventDefault()
@ -36,73 +80,8 @@ const AdvancedSearchForm = {
toggle () {
this.expand = !this.expand
},
// To generate mock Form.Item
getFields () {
const count = this.expand ? 10 : 6
const { getFieldDecorator } = this.form
const children = []
for (let i = 0; i < 10; i++) {
children.push(
<a-col span={8} key={i} style={{ display: i < count ? 'block' : 'none' }}>
<a-form-item label={`Field ${i}`}>
{getFieldDecorator(`field-${i}`, {
rules: [{
required: true,
message: 'Input something!',
}],
})(
<a-input placeholder='placeholder' />
)}
</a-form-item>
</a-col>
)
}
return children
},
},
render () {
return (
<a-form
class='ant-advanced-search-form'
onSubmit={this.handleSearch}
>
<a-row gutter={24}>{this.getFields()}</a-row>
<a-row>
<a-col span={24} style={{ textAlign: 'right' }}>
<a-button type='primary' htmlType='submit'>Search</a-button>
<a-button style={{ marginLeft: '8px' }} onClick={this.handleReset}>
Clear
</a-button>
<a style={{ marginLeft: '8px', fontSize: '12px' }} onClick={this.toggle}>
Collapse <a-icon type={this.expand ? 'up' : 'down'} />
</a>
</a-col>
</a-row>
</a-form>
)
},
}
const WrappedAdvancedSearchForm = Form.create()(AdvancedSearchForm)
export default {
methods: {
saveFormRef (inst) {
this.formRef = inst
},
},
render () {
return (
<div id='components-form-demo-advanced-search'>
<WrappedAdvancedSearchForm wrappedComponentRef={(inst) => this.saveFormRef(inst)}/>
<div class='search-result-list'>Search Result List</div>
</div>
)
},
}
</script>
<style>
.ant-advanced-search-form {

View File

@ -10,24 +10,29 @@ Use `setFieldsValue` to set other control's value programmaticly.
<template>
<a-form @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<a-form @submit="handleSubmit" :form="form">
<a-form-item
label='Note'
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 12 }"
fieldDecoratorId="note"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your note!' }]}"
>
<a-input />
<a-input
v-decorator="[
'note',
{rules: [{ required: true, message: 'Please input your note!' }]}
]"
/>
</a-form-item>
<a-form-item
label='Gender'
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 12 }"
fieldDecoratorId="gender"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please select your gender!' }]}"
>
<a-select
v-decorator="[
'gender',
{rules: [{ required: true, message: 'Please select your gender!' }]}
]"
placeholder='Select a option and change input text above'
@change="this.handleSelectChange"
>
@ -50,6 +55,7 @@ export default {
data () {
return {
formLayout: 'horizontal',
form: this.$form.createForm(this),
}
},
methods: {

View File

@ -15,9 +15,26 @@ Customized or third-party form controls can be used in Form, too. Controls must
</us>
<script>
import { Form } from 'ant-design-vue'
<template>
<a-form layout='inline' @submit="handleSubmit" :form="form">
<a-form-item label='Price'>
<price-input
v-decorator="[
'price',
{
initialValue: { number: 0, currency: 'rmb' },
rules: [{ validator: checkPrice }],
}
]"
/>
</a-form-item>
<a-form-item>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
</template>
<script>
const hasProp = (instance, prop) => {
const $options = instance.$options || {}
const propsData = $options.propsData || {}
@ -25,6 +42,24 @@ const hasProp = (instance, prop) => {
}
const PriceInput = {
props: ['value'],
template: `
<span>
<a-input
type='text'
:value="number"
@change="handleNumberChange"
style="width: 63%; margin-right: 2%;"
/>
<a-select
:value="currency"
style="width: 32%"
@change="handleCurrencyChange"
>
<a-select-option value='rmb'>RMB</a-select-option>
<a-select-option value='dollar'>Dollar</a-select-option>
</a-select>
</span>
`,
data () {
const value = this.value || {}
return {
@ -56,35 +91,16 @@ const PriceInput = {
this.triggerChange({ currency })
},
triggerChange (changedValue) {
// Should provide an event to pass value to Form.
// Should provide an event to pass value to Form.
this.$emit('change', Object.assign({}, this.$data, changedValue))
},
},
render () {
const { number, currency } = this
return (
<span>
<a-input
type='text'
value={number}
onChange={this.handleNumberChange}
style={{ width: '65%', marginRight: '3%' }}
/>
<a-select
value={currency}
style={{ width: '32%' }}
onChange={this.handleCurrencyChange}
>
<a-select-option value='rmb'>RMB</a-select-option>
<a-select-option value='dollar'>Dollar</a-select-option>
</a-select>
</span>
)
},
}
const Demo = {
export default {
beforeCreate () {
this.form = this.$form.createForm(this)
},
methods: {
handleSubmit (e) {
e.preventDefault()
@ -102,26 +118,11 @@ const Demo = {
callback('Price must greater than zero!')
},
},
render () {
const { getFieldDecorator } = this.form
return (
<a-form layout='inline' onSubmit={this.handleSubmit}>
<a-form-item label='Price'>
{getFieldDecorator('price', {
initialValue: { number: 0, currency: 'rmb' },
rules: [{ validator: this.checkPrice }],
})(<PriceInput />)}
</a-form-item>
<a-form-item>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
)
components: {
PriceInput,
},
}
export default Form.create()(Demo)
</script>

View File

@ -9,11 +9,76 @@ Add or remove form items dynamically.
</us>
<script>
import { Form } from 'ant-design-vue'
<template>
<a-form @submit="handleSubmit" :form="form">
<a-form-item
v-for="(k, index) in form.getFieldValue('keys')"
v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
:label="index === 0 ? 'Passengers' : ''"
:required="false"
:key="k"
>
<a-input
v-decorator="[
`names[${k}]`,
{
validateTrigger: ['change', 'blur'],
rules: [{
required: true,
whitespace: true,
message: 'Please input passenger\'s name or delete this field.',
}],
}
]"
placeholder='passenger name'
style="width: 60%; margin-right: 8px"
/>
<a-icon
v-if="form.getFieldValue('keys').length > 1"
class='dynamic-delete-button'
type='minus-circle-o'
:disabled="form.getFieldValue('keys').length === 1"
@click="() => remove(k)"
/>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type='dashed' @click="add" style="width: 60%">
<a-icon type='plus' /> Add field
</a-button>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
</template>
<script>
let uuid = 0
const DynamicFieldSet = {
export default {
beforeCreate () {
this.form = this.$form.createForm(this)
this.form.getFieldDecorator('keys', { initialValue: [] })
},
data () {
return {
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
},
formItemLayoutWithOutLabel: {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 },
},
},
}
},
methods: {
remove (k) {
const { form } = this
@ -52,73 +117,7 @@ const DynamicFieldSet = {
})
},
},
render () {
const { getFieldDecorator, getFieldValue } = this.form
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
}
const formItemLayoutWithOutLabel = {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 },
},
}
getFieldDecorator('keys', { initialValue: [] })
const keys = getFieldValue('keys')
const formItems = keys.map((k, index) => {
return (
<a-form-item
{...{ props: (index === 0 ? formItemLayout : formItemLayoutWithOutLabel) }}
label={index === 0 ? 'Passengers' : ''}
required={false}
key={k}
>
{getFieldDecorator(`names[${k}]`, {
validateTrigger: ['onChange', 'onBlur'],
rules: [{
required: true,
whitespace: true,
message: "Please input passenger's name or delete this field.",
}],
})(
<a-input placeholder='passenger name' style={{ width: '60%', marginRight: '8px' }} />
)}
{keys.length > 1 ? (
<a-icon
class='dynamic-delete-button'
type='minus-circle-o'
disabled={keys.length === 1}
onClick={() => this.remove(k)}
/>
) : null}
</a-form-item>
)
})
return (
<a-form onSubmit={this.handleSubmit}>
{formItems}
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
<a-button type='dashed' onClick={this.add} style={{ width: '60%' }}>
<a-icon type='plus' /> Add field
</a-button>
</a-form-item>
<a-form-item {...{ props: formItemLayoutWithOutLabel }}>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(DynamicFieldSet)
</script>
<style>
.dynamic-delete-button {

View File

@ -10,24 +10,32 @@ Perform different check rules according to different situations.
<template>
<a-form :autoFormCreate="(form)=>{this.form = form}">
<a-form :form="form">
<a-form-item
:labelCol="formItemLayout.labelCol"
:wrapperCol="formItemLayout.wrapperCol"
label='Name'
fieldDecoratorId="username"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your name' }]}"
>
<a-input placeholder='Please input your name' />
<a-input
v-decorator="[
'username',
{rules: [{ required: true, message: 'Please input your name' }]}
]"
placeholder='Please input your name'
/>
</a-form-item>
<a-form-item
:labelCol="formItemLayout.labelCol"
:wrapperCol="formItemLayout.wrapperCol"
label='Nickname'
fieldDecoratorId="nickname"
:fieldDecoratorOptions="{rules: [{ required: checkNick, message: 'Please input your nickname' }]}"
>
<a-input placeholder='Please input your nickname' />
<a-input
v-decorator="[
'nickname',
{rules: [{ required: checkNick, message: 'Please input your nickname' }]}
]"
placeholder='Please input your nickname'
/>
</a-form-item>
<a-form-item
:labelCol="formTailLayout.labelCol"
@ -64,6 +72,7 @@ export default {
checkNick: false,
formItemLayout,
formTailLayout,
form: this.$form.createForm(this),
}
},
methods: {

View File

@ -8,51 +8,66 @@
When user visit a page with a list of items, and want to create a new item. The page can popup a form in Modal, then let user fill in the form to create an item.
</us>
<template>
<div>
<a-button type='primary' @click="showModal">New Collection</a-button>
<collection-create-form
ref="collectionForm"
:visible="visible"
@cancel="handleCancel"
@create="handleCreate"
/>
</div>
</template>
<script>
import { Form } from 'ant-design-vue'
const CollectionCreateForm = Form.create()(
{
props: ['visible'],
render () {
const { visible, form } = this
const { getFieldDecorator } = form
return (
<a-modal
visible={visible}
title='Create a new collection'
okText='Create'
onCancel={() => { this.$emit('cancel') }}
onOk={() => { this.$emit('create') }}
>
<a-form layout='vertical'>
<a-form-item label='Title'>
{getFieldDecorator('title', {
const CollectionCreateForm = {
props: ['visible'],
beforeCreate () {
this.form = this.$form.createForm(this)
},
template: `
<a-modal
:visible="visible"
title='Create a new collection'
okText='Create'
@cancel="() => { $emit('cancel') }"
@ok="() => { $emit('create') }"
>
<a-form layout='vertical' :form="form">
<a-form-item label='Title'>
<a-input
v-decorator="[
'title',
{
rules: [{ required: true, message: 'Please input the title of collection!' }],
})(
<a-input />
)}
</a-form-item>
<a-form-item label='Description'>
{getFieldDecorator('description')(<a-input type='textarea' />)}
</a-form-item>
<a-form-item class='collection-create-form_last-form-item'>
{getFieldDecorator('modifier', {
initialValue: 'public',
})(
<a-radio-group>
<a-radio value='public'>Public</a-radio>
<a-radio value='private'>Private</a-radio>
</a-radio-group>
)}
</a-form-item>
</a-form>
</a-modal>
)
},
}
)
}
]"
/>
</a-form-item>
<a-form-item label='Description'>
<a-input
type='textarea'
v-decorator="['description']"
/>
</a-form-item>
<a-form-item class='collection-create-form_last-form-item'>
<a-radio-group
v-decorator="[
'modifier',
{
initialValue: 'private',
}
]"
>
<a-radio value='public'>Public</a-radio>
<a-radio value='private'>Private</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
`,
}
export default {
data () {
@ -68,38 +83,22 @@ export default {
this.visible = false
},
handleCreate () {
const form = this.formRef.form
const form = this.$refs.collectionForm.form
form.validateFields((err, values) => {
if (err) {
return
}
console.log('Received values of form: ', values)
form.resetFields()
this.visible = false
})
},
saveFormRef (formRef) {
this.formRef = formRef
},
},
render () {
return (
<div>
<a-button type='primary' onClick={this.showModal}>New Collection</a-button>
<CollectionCreateForm
wrappedComponentRef={this.saveFormRef}
visible={this.visible}
onCancel={this.handleCancel}
onCreate={this.handleCreate}
/>
</div>
)
},
components: { CollectionCreateForm },
}
</script>

View File

@ -3,52 +3,78 @@
通过使用 `onFieldsChange` `mapPropsToFields`可以把表单的数据存储到上层组件
**注意**
`mapPropsToFields` 里面返回的表单域数据必须使用 `Form.createFormField` 包装
上层组件传递的属性必须在`Form.create({ props: ...})`的props中声明
如果你使用`Form.create`上层组件传递的属性必须在`Form.create({ props: ...})`的props中声明
如果使用`this.$form.createForm`你可以使用任何数据不仅仅局限于上层组件的属性
</cn>
<us>
#### Store Form Data into Upper Component
We can store form data into upper component.
**Note:**
You must wrap field data with `Form.createFormField` in `mapPropsToFields`.
If you use `Form.create`. You must wrap field data with `Form.createFormField` in `mapPropsToFields`.
The properties passed by the upper component must be declared in the props of `Form.create({ props: ...})`.
But if you use `this.$form.createForm`, You can use any data, not just the properties of the upper components.
</us>
<template>
<div id='components-form-demo-global-state'>
<customized-form
:username="fields.username"
@change="handleFormChange"
/>
<pre class='language-bash'>
{{JSON.stringify(fields, null, 2)}}
</pre>
</div>
</template>
<script>
import { Form } from 'ant-design-vue'
const CustomizedForm = Form.create({
props: ['username'], // must declare like vue `props` https://vuejs.org/v2/api/#props
onFieldsChange (instance, changedFields) {
instance.$emit('change', changedFields)
const CustomizedForm = {
props: ['username'],
template: `
<a-form layout='inline' :form="form">
<a-form-item label='Username'>
<a-input
v-decorator="[
'username',
{
rules: [{ required: true, message: 'Username is required!' }],
}
]"
/>
</a-form-item>
</a-form>
`,
created () {
this.form = this.$form.createForm(this, {
onFieldsChange: (_, changedFields) => {
this.$emit('change', changedFields)
},
mapPropsToFields: () => {
return {
username: this.$form.createFormField({
...this.username,
value: this.username.value,
}),
}
},
onValuesChange (_, values) {
console.log(values)
},
})
},
mapPropsToFields (props) {
return {
username: Form.createFormField({
...props.username,
value: props.username.value,
}),
}
watch: {
username () {
this.form.updateFields({
username: this.$form.createFormField({
...this.username,
value: this.username.value,
}),
})
},
},
onValuesChange (_, values) {
console.log(values)
},
})({
render () {
const { getFieldDecorator } = this.form
return (
<a-form layout='inline'>
<a-form-item label='Username'>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Username is required!' }],
})(<a-input />)}
</a-form-item>
</a-form>
)
},
})
}
export default {
data () {
@ -62,20 +88,12 @@ export default {
},
methods: {
handleFormChange (changedFields) {
console.log('changedFields', changedFields)
this.fields = { ...this.fields, ...changedFields }
},
},
render () {
const fields = this.fields
return (
<div id='components-form-demo-global-state'>
<CustomizedForm {...{ props: fields }} onChange={this.handleFormChange} />
<pre class='language-bash'>
{JSON.stringify(fields, null, 2)}
</pre>
</div>
)
components: {
CustomizedForm,
},
}
</script>
@ -90,3 +108,4 @@ export default {

View File

@ -10,38 +10,45 @@ Horizontal login form is often used in navigation bar.
<template>
<a-form layout='inline' @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<template v-if="form">
<a-form-item
:validateStatus="userNameError() ? 'error' : ''"
:help="userNameError() || ''"
fieldDecoratorId="userName"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your username!' }]}"
<a-form layout='inline' @submit="handleSubmit" :form="form">
<a-form-item
:validateStatus="userNameError() ? 'error' : ''"
:help="userNameError() || ''"
>
<a-input
placeholder='Username'
v-decorator="[
'userName',
{rules: [{ required: true, message: 'Please input your username!' }]}
]"
>
<a-input placeholder='Username'>
<a-icon slot="prefix" type='user' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item
:validateStatus="passwordError() ? 'error' : ''"
:help="passwordError() || ''"
fieldDecoratorId="password"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your Password!' }]}"
<a-icon slot="prefix" type='user' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item
:validateStatus="passwordError() ? 'error' : ''"
:help="passwordError() || ''"
>
<a-input
v-decorator="[
'password',
{rules: [{ required: true, message: 'Please input your Password!' }]}
]"
type='password'
placeholder='Password'
>
<a-input type='password' placeholder='Password'>
<a-icon slot="prefix" type='lock' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type='primary'
htmlType='submit'
:disabled="hasErrors(form.getFieldsError())"
>
Log in
</a-button>
</a-form-item>
</template>
<a-icon slot="prefix" type='lock' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type='primary'
htmlType='submit'
:disabled="hasErrors(form.getFieldsError())"
>
Log in
</a-button>
</a-form-item>
</a-form>
</template>
@ -53,7 +60,7 @@ export default {
data () {
return {
hasErrors,
form: null,
form: this.$form.createForm(this),
}
},
mounted () {

View File

@ -8,11 +8,58 @@
Normal login form which can contain more elements.
</us>
<template>
<a-form :form="form" id='components-form-demo-normal-login' @submit="handleSubmit" class='login-form'>
<a-form-item>
<a-input
placeholder='Username'
v-decorator="[
'userName',
{ rules: [{ required: true, message: 'Please input your username!' }] }
]"
>
<a-icon slot="prefix" type='user' style="color: rgba(0,0,0,.25)" />
</a-input>
</a-form-item>
<a-form-item>
<a-input
v-decorator="[
'password',
{ rules: [{ required: true, message: 'Please input your Password!' }] }
]"
type='password'
placeholder='Password'
>
<a-icon slot="prefix" type='lock' style="color: rgba(0,0,0,.25)" />
</a-input>
</a-form-item>
<a-form-item>
<a-checkbox
v-decorator="[
'remember',
{
valuePropName: 'checked',
initialValue: true,
}
]"
>
Remember me
</a-checkbox>
<a class='login-form-forgot' href=''>Forgot password</a>
<a-button type='primary' htmlType='submit' class='login-form-button'>
Log in
</a-button>
Or <a href=''>register now!</a>
</a-form-item>
</a-form>
</template>
<script>
import { Form } from 'ant-design-vue'
const NormalLoginForm = {
export default {
beforeCreate () {
this.form = this.$form.createForm(this)
},
methods: {
handleSubmit (e) {
e.preventDefault()
@ -23,44 +70,7 @@ const NormalLoginForm = {
})
},
},
render () {
const { getFieldDecorator } = this.form
return (
<a-form id='components-form-demo-normal-login' onSubmit={this.handleSubmit} class='login-form'>
<a-form-item>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<a-input prefix={<a-icon type='user' style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder='Username' />
)}
</a-form-item>
<a-form-item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<a-input prefix={<a-icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />} type='password' placeholder='Password' />
)}
</a-form-item>
<a-form-item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<a-checkbox>Remember me</a-checkbox>
)}
<a class='login-form-forgot' href=''>Forgot password</a>
<a-button type='primary' htmlType='submit' class='login-form-button'>
Log in
</a-button>
Or <a href=''>register now!</a>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(NormalLoginForm)
</script>
<style>
#components-form-demo-normal-login .login-form {
@ -77,3 +87,4 @@ export default Form.create()(NormalLoginForm)

View File

@ -8,10 +8,170 @@
Fill in this form to create a new account for you.
</us>
<template>
<a-form @submit="handleSubmit" :form="form">
<a-form-item
v-bind="formItemLayout"
label='E-mail'
>
<a-input
v-decorator="[
'email',
{
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}]
}
]"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Password'
>
<a-input
v-decorator="[
'password',
{
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
}
]"
type='password'
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Confirm Password'
>
<a-input
v-decorator="[
'confirm',
{
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: compareToFirstPassword,
}],
}
]"
type='password'
@blur="handleConfirmBlur"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
>
<span slot="label">
Nickname&nbsp;
<a-tooltip title='What do you want others to call you?'>
<a-icon type='question-circle-o' />
</a-tooltip>
</span>
<a-input
v-decorator="[
'nickname',
{
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }]
}
]"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Habitual Residence'
>
<a-cascader
v-decorator="[
'residence',
{
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
}
]"
:options="residences"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Phone Number'
>
<a-input
v-decorator="[
'phone',
{
rules: [{ required: true, message: 'Please input your phone number!' }],
}
]"
style="width: 100%"
>
<a-select
v-decorator="[
'prefix',
{ initialValue: '86' }
]"
slot="addonBefore"
style="width: 70px"
>
<a-select-option value='86'>+86</a-select-option>
<a-select-option value='87'>+87</a-select-option>
</a-select>
</a-input>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Website'
>
<a-auto-complete
v-decorator="[
'website',
{rules: [{ required: true, message: 'Please input website!' }]}
]"
@change="handleWebsiteChange"
placeholder='website'
>
<template slot="dataSource">
<a-select-option v-for="website in autoCompleteResult" :key="website">{{website}}</a-select-option>
</template>
<a-input />
</a-auto-complete>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Captcha'
extra='We must make sure that your are a human.'
>
<a-row :gutter="8">
<a-col :span="12">
<a-input
v-decorator="[
'captcha',
{rules: [{ required: true, message: 'Please input the captcha you got!' }]}
]"
/>
</a-col>
<a-col :span="12">
<a-button>Get captcha</a-button>
</a-col>
</a-row>
</a-form-item>
<a-form-item v-bind="tailFormItemLayout">
<a-checkbox
v-decorator="['agreement', {valuePropName: 'checked'}]"
>I have read the <a href=''>agreement</a></a-checkbox>
</a-form-item>
<a-form-item v-bind="tailFormItemLayout">
<a-button type='primary' htmlType='submit'>Register</a-button>
</a-form-item>
</a-form>
</template>
<script>
import { Form } from 'ant-design-vue'
const residences = [{
value: 'zhejiang',
label: 'Zhejiang',
@ -36,11 +196,37 @@ const residences = [{
}],
}]
const RegistrationForm = {
export default {
beforeCreate () {
this.form = this.$form.createForm(this)
},
data () {
return {
confirmDirty: false,
residences,
autoCompleteResult: [],
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
},
tailFormItemLayout: {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
},
}
},
methods: {
@ -81,180 +267,10 @@ const RegistrationForm = {
this.autoCompleteResult = autoCompleteResult
},
},
render () {
const { getFieldDecorator } = this.form
const { autoCompleteResult } = this
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
}
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
}
const prefixSelector = getFieldDecorator('prefix', {
initialValue: '86',
})(
<a-select style={{ width: '70px' }}>
<a-select-option value='86'>+86</a-select-option>
<a-select-option value='87'>+87</a-select-option>
</a-select>
)
const websiteOptions = autoCompleteResult.map(website => (
<a-select-option key={website}>{website}</a-select-option>
))
return (
<a-form onSubmit={this.handleSubmit}>
<a-form-item
{...{ props: formItemLayout }}
label='E-mail'
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<a-input />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Password'
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
})(
<a-input type='password' />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Confirm Password'
>
{getFieldDecorator('confirm', {
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: this.compareToFirstPassword,
}],
})(
<a-input type='password' onBlur={this.handleConfirmBlur} />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label={(
<span>
Nickname&nbsp;
<a-tooltip title='What do you want others to call you?'>
<a-icon type='question-circle-o' />
</a-tooltip>
</span>
)}
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(
<a-input />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Habitual Residence'
>
{getFieldDecorator('residence', {
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
})(
<a-cascader options={residences} />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Phone Number'
>
{getFieldDecorator('phone', {
rules: [{ required: true, message: 'Please input your phone number!' }],
})(
<a-input addonBefore={prefixSelector} style={{ width: '100%' }} />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Website'
>
{getFieldDecorator('website', {
rules: [{ required: true, message: 'Please input website!' }],
})(
<a-auto-complete
dataSource={websiteOptions}
onChange={this.handleWebsiteChange}
placeholder='website'
>
<a-input />
</a-auto-complete>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Captcha'
extra='We must make sure that your are a human.'
>
<a-row gutter={8}>
<a-col span={12}>
{getFieldDecorator('captcha', {
rules: [{ required: true, message: 'Please input the captcha you got!' }],
})(
<a-input />
)}
</a-col>
<a-col span={12}>
<a-button>Get captcha</a-button>
</a-col>
</a-row>
</a-form-item>
<a-form-item {...{ props: tailFormItemLayout }}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<a-checkbox>I have read the <a href=''>agreement</a></a-checkbox>
)}
</a-form-item>
<a-form-item {...{ props: tailFormItemLayout }}>
<a-button type='primary' htmlType='submit'>Register</a-button>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(RegistrationForm)
</script>

View File

@ -8,15 +8,83 @@
the `value` of time-related components is `moment`. So, we need to pre-process those values.
</us>
<template>
<a-form @submit="handleSubmit" :form="form">
<a-form-item
v-bind="formItemLayout"
label='DatePicker'
>
<a-date-picker v-decorator="['date-picker', config]"/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='DatePicker[showTime]'
>
<a-date-picker v-decorator="['date-time-picker', config]" showTime format='YYYY-MM-DD HH:mm:ss' />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='MonthPicker'
>
<a-monthPicker v-decorator="['month-picker', config]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='RangePicker'
>
<a-range-picker v-decorator="['range-picker', rangeConfig]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='RangePicker[showTime]'
>
<a-range-picker v-decorator="['range-time-picker', rangeConfig]" showTime format='YYYY-MM-DD HH:mm:ss' />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='TimePicker'
>
<a-time-picker v-decorator="['time-picker', config]" />
</a-form-item>
<a-form-item
:wrapperCol="{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
}"
>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
</template>
<script>
import { Form } from 'ant-design-vue'
const TimeRelatedForm = {
export default {
beforeCreate () {
this.form = this.$form.createForm(this)
},
data () {
window.form = this
return {
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
},
config: {
rules: [{ type: 'object', required: true, message: 'Please select time!' }],
},
rangeConfig: {
rules: [{ type: 'array', required: true, message: 'Please select time!' }],
},
}
},
methods: {
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, fieldsValue) => {
if (err) {
return
@ -41,91 +109,10 @@ const TimeRelatedForm = {
})
},
},
render () {
const { getFieldDecorator } = this.form
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
}
const config = {
rules: [{ type: 'object', required: true, message: 'Please select time!' }],
}
const rangeConfig = {
rules: [{ type: 'array', required: true, message: 'Please select time!' }],
}
return (
<a-form onSubmit={this.handleSubmit}>
<a-form-item
{...{ props: formItemLayout }}
label='DatePicker'
>
{getFieldDecorator('date-picker', config)(
<a-date-picker />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='DatePicker[showTime]'
>
{getFieldDecorator('date-time-picker', config)(
<a-date-picker showTime format='YYYY-MM-DD HH:mm:ss' />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='MonthPicker'
>
{getFieldDecorator('month-picker', config)(
<a-monthPicker />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='RangePicker'
>
{getFieldDecorator('range-picker', rangeConfig)(
<a-range-picker />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='RangePicker[showTime]'
>
{getFieldDecorator('range-time-picker', rangeConfig)(
<a-range-picker showTime format='YYYY-MM-DD HH:mm:ss' />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='TimePicker'
>
{getFieldDecorator('time-picker', config)(
<a-time-picker />
)}
</a-form-item>
<a-form-item
wrapperCol={{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
}}
>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(TimeRelatedForm)
</script>

View File

@ -9,10 +9,162 @@ Demostration for validataion configuration for form controls which are not show
</us>
<script>
import { Form } from 'ant-design-vue'
<template>
<a-form id='components-form-demo-validate-other' @submit="handleSubmit" :form="form">
<a-form-item
v-bind="formItemLayout"
label='Plain Text'
>
<span class='ant-form-text'>China</span>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Select'
hasFeedback
>
<a-select
v-decorator="[
'select',
{rules: [{ required: true, message: 'Please select your country!' }]}
]"
placeholder='Please select a country'
>
<a-select-option value='china'>China</a-select-option>
<a-select-option value='use'>U.S.A</a-select-option>
</a-select>
</a-form-item>
const Demo = {
<a-form-item
v-bind="formItemLayout"
label='Select[multiple]'
>
<a-select
v-decorator="[
'select-multiple', {
rules: [{ required: true, message: 'Please select your favourite colors!', type: 'array' }],
}]"
mode='multiple'
placeholder='Please select favourite colors'
>
<a-select-option value='red'>Red</a-select-option>
<a-select-option value='green'>Green</a-select-option>
<a-select-option value='blue'>Blue</a-select-option>
</a-select>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='InputNumber'
>
<a-input-number v-decorator="['input-number', { initialValue: 3 }]" :min="1" :max="10" />
<span class='ant-form-text'> machines</span>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Switch'
>
<a-switch v-decorator="['switch', { valuePropName: 'checked' }]"/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Slider'
>
<a-slider v-decorator="['slider']" :marks="{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Radio.Group'
>
<a-radio-group v-decorator="['radio-group']">
<a-radio value='a'>item 1</a-radio>
<a-radio value='b'>item 2</a-radio>
<a-radio value='c'>item 3</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Radio.Button'
>
<a-radio-group v-decorator="['radio-button']">
<a-radio-button value='a'>item 1</a-radio-button>
<a-radio-button value='b'>item 2</a-radio-button>
<a-radio-button value='c'>item 3</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Rate'
>
<a-rate allowHalf v-decorator="['rate', {initialValue: 3.5}]"/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Upload'
extra='longgggggggggggggggggggggggggggggggggg'
>
<a-upload
v-decorator="['upload', {
valuePropName: 'fileList',
getValueFromEvent: normFile,
}]"
name='logo'
action='/upload.do'
listType='picture'
>
<a-button>
<a-icon type='upload' /> Click to upload
</a-button>
</a-upload>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label='Dragger'
>
<div class='dropbox'>
<a-upload-dragger
v-decorator="['dragger', {
valuePropName: 'fileList',
getValueFromEvent: normFile,
}]"
name='files'
action='/upload.do'
>
<p class='ant-upload-drag-icon'>
<a-icon type='inbox' />
</p>
<p class='ant-upload-text'>Click or drag file to this area to upload</p>
<p class='ant-upload-hint'>Support for a single or bulk upload.</p>
</a-upload-dragger>
</div>
</a-form-item>
<a-form-item
:wrapperCol="{ span: 12, offset: 6 }"
>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
beforeCreate () {
window.test = this
this.form = this.$form.createForm(this)
},
data: () => ({
formItemLayout: {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
},
}),
methods: {
handleSubmit (e) {
e.preventDefault()
@ -30,168 +182,7 @@ const Demo = {
return e && e.fileList
},
},
render () {
const { getFieldDecorator } = this.form
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
}
return (
<a-form id='components-form-demo-validate-other' onSubmit={this.handleSubmit}>
<a-form-item
{...{ props: formItemLayout }}
label='Plain Text'
>
<span class='ant-form-text'>China</span>
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Select'
hasFeedback
>
{getFieldDecorator('select', {
rules: [
{ required: true, message: 'Please select your country!' },
],
})(
<a-select placeholder='Please select a country'>
<a-select-option value='china'>China</a-select-option>
<a-select-option value='use'>U.S.A</a-select-option>
</a-select>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Select[multiple]'
>
{getFieldDecorator('select-multiple', {
rules: [
{ required: true, message: 'Please select your favourite colors!', type: 'array' },
],
})(
<a-select mode='multiple' placeholder='Please select favourite colors'>
<a-select-option value='red'>Red</a-select-option>
<a-select-option value='green'>Green</a-select-option>
<a-select-option value='blue'>Blue</a-select-option>
</a-select>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='InputNumber'
>
{getFieldDecorator('input-number', { initialValue: 3 })(
<a-input-number min={1} max={10} />
)}
<span class='ant-form-text'> machines</span>
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Switch'
>
{getFieldDecorator('switch', { valuePropName: 'checked' })(
<a-switch />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Slider'
>
{getFieldDecorator('slider')(
<a-slider marks={{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }} />
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Radio.Group'
>
{getFieldDecorator('radio-group')(
<a-radio-group>
<a-radio value='a'>item 1</a-radio>
<a-radio value='b'>item 2</a-radio>
<a-radio value='c'>item 3</a-radio>
</a-radio-group>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Radio.Button'
>
{getFieldDecorator('radio-button')(
<a-radio-group>
<a-radio-button value='a'>item 1</a-radio-button>
<a-radio-button value='b'>item 2</a-radio-button>
<a-radio-button value='c'>item 3</a-radio-button>
</a-radio-group>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Rate'
>
{getFieldDecorator('rate', {
initialValue: 3.5,
})(
<a-rate allowHalf/>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Upload'
extra='longgggggggggggggggggggggggggggggggggg'
>
{getFieldDecorator('upload', {
valuePropName: 'fileList',
getValueFromEvent: this.normFile,
})(
<a-upload name='logo' action='/upload.do' listType='picture'>
<a-button>
<a-icon type='upload' /> Click to upload
</a-button>
</a-upload>
)}
</a-form-item>
<a-form-item
{...{ props: formItemLayout }}
label='Dragger'
>
<div class='dropbox'>
{getFieldDecorator('dragger', {
valuePropName: 'fileList',
getValueFromEvent: this.normFile,
})(
<a-upload-dragger name='files' action='/upload.do'>
<p class='ant-upload-drag-icon'>
<a-icon type='inbox' />
</p>
<p class='ant-upload-text'>Click or drag file to this area to upload</p>
<p class='ant-upload-hint'>Support for a single or bulk upload.</p>
</a-upload-dragger>
)}
</div>
</a-form-item>
<a-form-item
wrapperCol={{ span: 12, offset: 6 }}
>
<a-button type='primary' htmlType='submit'>Submit</a-button>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(Demo)
</script>
<style>
#components-form-demo-validate-other .dropbox {
@ -203,3 +194,4 @@ export default Form.create()(Demo)

View File

@ -1,64 +1,53 @@
````html
<Form.Item {...props}>
{children}
</Form.Item>
````
## API
### Form
| Property | Description | Type | Default Value |
| -------- | ----------- | ---- | ------------- |
| form | Decorated by `Form.create()` will be automatically set `this.form` property, so just pass to form, you don't need to set it by yourself after 1.7.0. | object | n/a |
| form | Decorated by `Form.create()` will be automatically set `this.form` property, so just pass to form. If you use the template syntax, you can use `this.$form.createForm(this, options)` | object | n/a |
| hideRequiredMark | Hide required mark of all form items | Boolean | false |
| layout | Define form layout(Support after 2.8) | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| autoFormCreate | Automate Form.create, Recommended for use under the `template` component, and cannot be used with `Form.create()` |Function(form)| |
| options | The `options` corresponding to `Form.create(options)` | Object | {} |
| autoFormCreate(deprecated) | Automate Form.create, Recommended for use under the `template` component, and cannot be used with `Form.create()`. You should use `$form.createForm` to instead it after 1.1.9. |Function(form)| |
| options(deprecated) | The `options` corresponding to `Form.create(options)`. You should use `$form.createForm` to instead it after 1.1.9. | Object | {} |
### Events
| Events Name | Description | Arguments |
| --- | --- | --- |
| submit | Defines a function will be called if form data validation is successful. | Function(e:Event) |
### autoFormCreate
````html
<a-form :autoFormCreate="(form)=>{this.form = form}">
...
</a-form>
````
If you use the `template` syntax, you can use ʻautoFormCreate` to turn on automatic validation and data collection, but each `Form.Item` only to `decorator` for its first child. More complex features suggest `JSX`.
Related examples are as follows:
[coordinated-controls](/ant-design-vue/components/form/#components-form-demo-coordinated-controls)
[dynamic-rules](/ant-design-vue/components/form/#components-form-demo-dynamic-rules)
[horizontal-login-form](/ant-design-vue/components/form/#components-form-demo-horizontal-login-form)
### Form.create(options)
### Form.create(options) \| this.$form.createForm(this, options)
How to use
#### Used in jsx, the usage is consistent with the React version of antd
```jsx
const CustomizedForm = {}
CustomizedForm = Form.create({})(CustomizedForm);
```
Maintain an ref for wrapped component instance, use `wrappedComponentRef`.
Example: [demo](#components-form-demo-advanced-search)
#### Single file template usage
````html
<template>
<a-form :form="form" />
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this, options)
},
}
</script>
````
The following `options` are available:
| Property | Description | Type |
| -------- | ----------- | ---- |
| props | 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) | (props) => Object{ fieldName: FormField { value } } |
| 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 } } |
| 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) |
| onValuesChange | A handler while value of any field is changed | (props, values) => void |
@ -69,7 +58,7 @@ If the form has been decorated by `Form.create` then it has `this.form` property
| Method | Description | Type |
| ------ | ----------- | ---- |
| getFieldDecorator | Two-way binding for form, please read below for details. | |
| getFieldDecorator | Two-way binding for form, single file template can be bound using the directive `v-decorator`. please read below for details. | |
| getFieldError | Get the error of a field. | Function(name) |
| getFieldsError | Get the specified fields' error. If you don't specify a parameter, you will get all fields' error. | Function(\[names: string\[]]) |
| getFieldsValue | Get the specified fields' values. If you don't specify a parameter, you will get all fields' values. | Function(\[fieldNames: string\[]]) |
@ -131,9 +120,9 @@ If the form has been decorated by `Form.create` then it has `this.form` property
To mark the returned fields data in `mapPropsToFields`, [demo](#components-form-demo-global-state).
### this.form.getFieldDecorator(id, options)
### this.form.getFieldDecorator(id, options) and v-decorator="[id, options]"
After wrapped by `getFieldDecorator`, `value`(or other property defined by `valuePropName`) `onChange`(or other property defined by `trigger`) props will be added to form controlsthe flow of form data will be handled by Form which will cause:
After wrapped by `getFieldDecorator` or `v-decorator`, `value`(or other property defined by `valuePropName`) `onChange`(or other property defined by `trigger`) props will be added to form controlsthe flow of form data will be handled by Form which will cause:
1. You shouldn't use `onChange` to collect data, but you still can listen to `onChange`(and so on) events.
2. You can not set value of form control via `value` `defaultValue` prop, and you should set default value with `initialValue` in `getFieldDecorator` instead.
@ -141,10 +130,10 @@ After wrapped by `getFieldDecorator`, `value`(or other property defined by `valu
#### Special attention
1. `getFieldDecorator` can not be used to decorate stateless component.
2. you can't use `getFieldDecorator` in stateless component: <https://vuejs.org/v2/api/#functional>
1. `getFieldDecorator` and `v-decorator` can not be used to decorate stateless component.
2. you can't use `getFieldDecorator` and `v-decorator` in stateless component: <https://vuejs.org/v2/api/#functional>
#### getFieldDecorator(id, options) parameters
#### getFieldDecorator(id, options) and v-decorator="[id, options]" parameters
| Property | Description | Type | Default Value |
| -------- | ----------- | ---- | ------------- |
@ -162,7 +151,7 @@ After wrapped by `getFieldDecorator`, `value`(or other property defined by `valu
Note:
- If Form.Item has multiple children that had been decorated by `getFieldDecorator`, `help` and `required` and `validateStatus` can't be generated automatically.
- If Form.Item has multiple children that had been decorated by `getFieldDecorator` or `v-decorator`, `help` and `required` and `validateStatus` can't be generated automatically.
| Property | Description | Type | Default Value |
| -------- | ----------- | ---- | ------------- |
@ -175,8 +164,6 @@ Note:
| required | Whether provided or not, it will be generated by the validation rule. | boolean | false |
| validateStatus | The validation status. If not provided, it will be generated by validation rule. options: 'success' 'warning' 'error' 'validating' | string | |
| wrapperCol | The layout for input controls, same as `labelCol` | [object](/ant-design-vue/components/grid/#Col) | |
| fieldDecoratorId | Corresponds to the first parameter `id` of `getFieldDecorator(id, options)`. If you need to collect data, you need to set this field. | string | |
| fieldDecoratorOptions | Corresponds to the second parameter `options` of `getFieldDecorator(id, options)`. | object | {} |
### Validation Rules

View File

@ -1,8 +1,10 @@
import Vue from 'vue'
import Form from './Form'
import ref from 'vue-ref'
import FormDecoratorDirective from '../_util/FormDecoratorDirective'
Vue.use(ref, { name: 'ant-ref' })
Vue.use(FormDecoratorDirective)
export { FormProps, FormCreateOption, ValidationRule } from './Form'
export { FormItemProps } from './FormItem'
@ -11,6 +13,7 @@ export { FormItemProps } from './FormItem'
Form.install = function (Vue) {
Vue.component(Form.name, Form)
Vue.component(Form.Item.name, Form.Item)
Vue.prototype.$form = Form
}
export default Form

View File

@ -1,64 +1,51 @@
````html
<Form.Item {...props}>
{children}
</Form.Item>
````
## API
### Form
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| form | 经 `Form.create()` 包装过的组件会自带 `this.form` 属性,直接传给 Form 即可。无需手动设置 | object | 无 |
| form | 经 `Form.create()` 包装过的组件会自带 `this.form` 属性如果使用template语法可以使用this.$form.createForm(this, options) | object | 无 |
| hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false |
| layout | 表单布局 | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| autoFormCreate | 自动执行Form.create建议在template组件下使用并且不可以和`Form.create()`同时使用 |Function(form)| 无|
| options | 对应Form.create(options)的`options` | Object | {} |
### 事件
| 事件名称 | 说明 | 回调参数 |
| --- | --- | --- |
| submit | 数据验证成功后回调事件 | Function(e:Event) |
### autoFormCreate
````html
<a-form :autoFormCreate="(form)=>{this.form = form}">
...
</a-form>
````
如果使用`template`语法,可以使用`autoFormCreate`开启自动校验和数据收集功能,但每一个`Form.Item`仅仅对其第一个子组件进行`decorator`。更加复杂的功能建议使用`JSX`。
相关示例如下:
[coordinated-controls](/ant-design-vue/components/form-cn/#components-form-demo-coordinated-controls)
[dynamic-rules](/ant-design-vue/components/form-cn/#components-form-demo-dynamic-rules)
[horizontal-login-form](/ant-design-vue/components/form-cn/#components-form-demo-horizontal-login-form)
### Form.create(options)
### Form.create(options) \| this.$form.createForm(this, options)
使用方式如下:
#### jsx使用方式使用方式和React版antd一致
```jsx
const CustomizedForm = {}
CustomizedForm = Form.create({})(CustomizedForm);
```
如果需要为包装组件实例维护一个ref可以使用`wrappedComponentRef`。
参考[示例](#components-form-demo-advanced-search)
#### 单文件template使用方式
````html
<template>
<a-form :form="form" />
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this, options)
},
}
</script>
````
`options` 的配置项如下。
| 参数 | 说明 | 类型 |
| --- | --- | --- |
| props | 父组件需要映射到表单项上的属性声明(和[vue组件props一致]( https://vuejs.org/v2/api/#props)) | {} |
| mapPropsToFields | 把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 [`Form.createFormField`](#Form.createFormField) 标记 | (props) => Object{ fieldName: FormField { value } } |
| props | 仅仅支持Form.create({})(CustomizedForm)的使用方式,父组件需要映射到表单项上的属性声明(和[vue组件props一致]( https://vuejs.org/v2/api/#props)) | {} |
| mapPropsToFields | 把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 [`Form.createFormField`](#Form.createFormField) 标记,如果使用$form.createForm创建收集器你可以将任何数据映射到Field中不受父组件约束 | (props) => Object{ fieldName: FormField { value } } |
| 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) |
| onValuesChange | 任一表单域的值发生改变时的回调 | (props, values) => void |
@ -69,7 +56,7 @@ CustomizedForm = Form.create({})(CustomizedForm);
| 方法      | 说明                                     | 类型       |
| ------- | -------------------------------------- | -------- |
| getFieldDecorator | 用于和表单进行双向绑定,详见下方描述 | |
| getFieldDecorator | 用于和表单进行双向绑定,单文件template可以使用指令`v-decorator`进行绑定,详见下方描述 | |
| getFieldError | 获取某个输入控件的 Error | Function(name) |
| getFieldsError | 获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 Error | Function(\[names: string\[]]) |
| getFieldsValue | 获取一组输入控件的值,如不传入参数,则获取全部组件的值 | Function(\[fieldNames: string\[]]) |
@ -131,9 +118,9 @@ CustomizedForm = Form.create({})(CustomizedForm);
用于标记 `mapPropsToFields` 返回的表单域数据,[例子](#components-form-demo-global-state)。
### this.form.getFieldDecorator(id, options)
### this.form.getFieldDecorator(id, options) 和 v-decorator="[id, options]"
经过 `getFieldDecorator` 包装的控件,表单控件会自动添加 `value`(或 `valuePropName` 指定的其他属性) `onChange`(或 `trigger` 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
经过 `getFieldDecorator`或`v-decorator` 包装的控件,表单控件会自动添加 `value`(或 `valuePropName` 指定的其他属性) `onChange`(或 `trigger` 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
1. 你**不再需要也不应该**用 `onChange` 来做同步,但还是可以继续监听 `onChange` 等事件。
2. 你不能用控件的 `value` `defaultValue` 等属性来设置表单域的值,默认值可以用 `getFieldDecorator` 里的 `initialValue`
@ -141,10 +128,10 @@ CustomizedForm = Form.create({})(CustomizedForm);
#### 特别注意
1. `getFieldDecorator` 不能用于装饰纯函数组件。
2. `getFieldDecorator` 调用不能位于纯函数组件中 <https://cn.vuejs.org/v2/api/#functional>
1. `getFieldDecorator`和`v-decorator` 不能用于装饰纯函数组件。
2. `getFieldDecorator`和`v-decorator` 调用不能位于纯函数组件中 <https://cn.vuejs.org/v2/api/#functional>
#### getFieldDecorator(id, options) 参数
#### getFieldDecorator(id, options) 和 v-decorator="[id, options]" 参数
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
@ -162,7 +149,7 @@ CustomizedForm = Form.create({})(CustomizedForm);
注意:
- 一个 Form.Item 建议只放一个被 getFieldDecorator 装饰过的 child当有多个被装饰过的 child 时,`help` `required` `validateStatus` 无法自动生成。
- 一个 Form.Item 建议只放一个被 getFieldDecorator或v-decorator 装饰过的 child当有多个被装饰过的 child 时,`help` `required` `validateStatus` 无法自动生成。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
@ -175,8 +162,6 @@ CustomizedForm = Form.create({})(CustomizedForm);
| required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false |
| validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/ant-design-vue/components/grid-cn/#Col) | |
| fieldDecoratorId | 对应`getFieldDecorator(id, options)`的第一个参数`id`,如需收集数据需要设置该字段 | string | 无 |
| fieldDecoratorOptions | 对应`getFieldDecorator(id, options)`的第二个参数`options` | object | {} |
### 校验规则

View File

@ -18,6 +18,7 @@ const getStepsProps = (defaultProps = {}) => {
PropTypes.bool,
PropTypes.func,
]),
labelPlacement: PropTypes.string.def('horizontal'),
}
return initDefaultProps(props, defaultProps)
}

View File

@ -10,31 +10,37 @@ export const TimelineProps = {
/** 指定最后一个幽灵节点是否存在或内容 */
pending: PropTypes.any,
pendingDot: PropTypes.string,
reverse: PropTypes.bool,
mode: PropTypes.oneOf(['left', 'alternate', 'right']),
}
export default {
name: 'ATimeline',
props: initDefaultProps(TimelineProps, {
prefixCls: 'ant-timeline',
reverse: false,
}),
render () {
const { prefixCls, ...restProps } = getOptionProps(this)
const { prefixCls, reverse, mode, ...restProps } = getOptionProps(this)
const pendingDot = getComponentFromProp(this, 'pendingDot')
const pending = getComponentFromProp(this, 'pending')
const pendingNode = typeof pending === 'boolean' ? null : pending
const classString = classNames(prefixCls, {
[`${prefixCls}-pending`]: !!pending,
[`${prefixCls}-reverse`]: !!reverse,
[`${prefixCls}-${mode}`]: !!mode,
})
// Remove falsy items
const falsylessItems = filterEmpty(this.$slots.default)
const items = falsylessItems.map((item, idx) => {
return cloneElement(item, {
props: {
last: falsylessItems.length - 1 === idx,
},
})
})
const pendingItem = (pending) ? (
const children = filterEmpty(this.$slots.default)
// // Remove falsy items
// const falsylessItems = filterEmpty(this.$slots.default)
// const items = falsylessItems.map((item, idx) => {
// return cloneElement(item, {
// props: {
// last: falsylessItems.length - 1 === idx,
// },
// })
// })
const pendingItem = !!pending ? (
<TimelineItem
pending={!!pending}
>
@ -44,6 +50,28 @@ export default {
{pendingNode}
</TimelineItem>
) : null
const timeLineItems = !!reverse
? [pendingItem, ...children.reverse()]
: [...children, pendingItem]
// Remove falsy items
const truthyItems = timeLineItems.filter(item => !!item)
const itemsCount = truthyItems.length
const lastCls = `${prefixCls}-item-last`
const items = truthyItems.map((ele, idx) =>
cloneElement(ele, {
class: classNames([
(!reverse && !!pending)
? (idx === itemsCount - 2) ? lastCls : ''
: (idx === itemsCount - 1) ? lastCls : '',
(mode === 'alternate')
? (idx % 2 === 0) ? `${prefixCls}-item-left` : `${prefixCls}-item-right`
: (mode === 'right') ? `${prefixCls}-item-right` : '',
]),
}),
)
const timelineProps = {
props: {
...restProps,
@ -54,7 +82,6 @@ export default {
return (
<ul {...timelineProps}>
{items}
{pendingItem}
</ul>
)
},

View File

@ -1,5 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/timeline/demo/alternate.md correctly 1`] = `
<ul class="ant-timeline ant-timeline-alternate">
<li class="ant-timeline-item ant-timeline-item-left">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Create a services site 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-green"></div>
<div class="ant-timeline-item-content">Solve initial network problems 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-left">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-custom ant-timeline-item-head-blue"><i class="anticon anticon-clock-circle-o" style="font-size: 16px;"></i></div>
<div class="ant-timeline-item-content">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
</div>
</li>
<li class="ant-timeline-item ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-red"></div>
<div class="ant-timeline-item-content">Network problems being solved 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-left">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Create a services site 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-last ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-custom ant-timeline-item-head-blue"><i class="anticon anticon-clock-circle-o" style="font-size: 16px;"></i></div>
<div class="ant-timeline-item-content">
Technical testing 2015-09-01
</div>
</li>
</ul>
`;
exports[`renders ./components/timeline/demo/basic.md correctly 1`] = `
<ul class="ant-timeline">
<li class="ant-timeline-item">
@ -17,7 +56,7 @@ exports[`renders ./components/timeline/demo/basic.md correctly 1`] = `
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Technical testing 2015-09-01</div>
</li>
<li class="ant-timeline-item">
<li class="ant-timeline-item ant-timeline-item-last">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Network problems being solved 2015-09-01</div>
@ -46,7 +85,7 @@ exports[`renders ./components/timeline/demo/color.md correctly 1`] = `
<p>Solve initial network problems 3 2015-09-01</p>
</div>
</li>
<li class="ant-timeline-item">
<li class="ant-timeline-item ant-timeline-item-last">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">
@ -80,7 +119,7 @@ exports[`renders ./components/timeline/demo/custom.md correctly 1`] = `
Technical testing 2015-09-01
</div>
</li>
<li class="ant-timeline-item">
<li class="ant-timeline-item ant-timeline-item-last">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Network problems being solved 2015-09-01</div>
@ -89,23 +128,52 @@ exports[`renders ./components/timeline/demo/custom.md correctly 1`] = `
`;
exports[`renders ./components/timeline/demo/pending.md correctly 1`] = `
<ul class="ant-timeline ant-timeline-pending">
<li class="ant-timeline-item">
<div>
<ul class="ant-timeline ant-timeline-pending">
<li class="ant-timeline-item">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Create a services site 2015-09-01</div>
</li>
<li class="ant-timeline-item">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Solve initial network problems 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-last">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Technical testing 2015-09-01</div>
</li>
<li class="ant-timeline-item ant-timeline-item-pending">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-custom ant-timeline-item-head-blue"><i class="anticon anticon-loading anticon-spin"></i></div>
<div class="ant-timeline-item-content">Recording...</div>
</li>
</ul> <button type="button" class="ant-btn ant-btn-primary" style="margin-top: 16px;"><span>Toggle Reverse</span></button>
</div>
`;
exports[`renders ./components/timeline/demo/right.md correctly 1`] = `
<ul class="ant-timeline ant-timeline-right">
<li class="ant-timeline-item ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Create a services site 2015-09-01</div>
</li>
<li class="ant-timeline-item">
<li class="ant-timeline-item ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Solve initial network problems 2015-09-01</div>
</li>
<li class="ant-timeline-item">
<li class="ant-timeline-item ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-blue"></div>
<div class="ant-timeline-item-content">Technical testing 2015-09-01</div>
<div class="ant-timeline-item-head ant-timeline-item-head-custom ant-timeline-item-head-blue"><i class="anticon anticon-clock-circle-o" style="font-size: 16px;"></i></div>
<div class="ant-timeline-item-content">
Technical testing 2015-09-01
</div>
</li>
<li class="ant-timeline-item ant-timeline-item-pending">
<li class="ant-timeline-item ant-timeline-item-last ant-timeline-item-right">
<div class="ant-timeline-item-tail"></div>
<div class="ant-timeline-item-head ant-timeline-item-head-custom ant-timeline-item-head-blue"><i class="anticon anticon-loading"><svg viewBox="0 0 1024 1024" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" class="anticon-spin">
<path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path>

View File

@ -0,0 +1,30 @@
<cn>
#### 交替展现
内容在时间轴两侧轮流出现。
</cn>
<us>
#### Alternate
Alternate timeline.
</us>
```html
<template>
<a-timeline mode="alternate">
<a-timeline-item>Create a services site 2015-09-01</a-timeline-item>
<a-timeline-item color="green">Solve initial network problems 2015-09-01</a-timeline-item>
<a-timeline-item>
<a-icon slot="dot" type="clock-circle-o" style="font-size: 16px;" />
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
</a-timeline-item>
<a-timeline-item color="red">Network problems being solved 2015-09-01</a-timeline-item>
<a-timeline-item>Create a services site 2015-09-01</a-timeline-item>
<a-timeline-item>
<a-icon slot="dot" type="clock-circle-o" style="font-size: 16px;" />
Technical testing 2015-09-01
</a-timeline-item>
</a-timeline>
</template>
```

View File

@ -3,6 +3,8 @@ import Basic from './basic.md'
import Color from './color.md'
import Pending from './pending.md'
import Custom from './custom.md'
import Alternate from './alternate'
import Right from './right'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
@ -32,15 +34,12 @@ export default {
return (
<div>
<md cn={md.cn} us={md.us}/>
<br/>
<Basic />
<br/>
<Color />
<br/>
<Pending />
<br/>
<Custom />
<br/>
<Alternate />
<Right />
<api>
<template slot='cn'>
<CN/>

View File

@ -1,21 +1,39 @@
<cn>
#### 最后一个
当任务状态正在发生,还在记录过程中,可用幽灵节点来表示当前的时间节点(用于时间正序排列)。当 pending 值为 false ,可用定制元件替换默认时间图点
#### 最后一个及排序
当任务状态正在发生,还在记录过程中,可用幽灵节点来表示当前的时间节点,当 pending 为真值时展示幽灵节点,如果 pending 是 React 元素可用于定制该节点内容,同时 pendingDot 将可以用于定制其轴点。reverse 属性用于控制节点排序,为 false 时按正序排列,为 true 时按倒序排列
</cn>
<us>
#### Last node
When the timeline is incomplete and ongoing, put a ghost node at last. set `pending={true}` or `pending={a VNode Element}` or `slot="pending"`. Used in ascend chronological order. When `pending` is not false, set `pendingDot={a VNode Element}` or `slot="pendingDot"` to replace the default pending dot.
When the timeline is incomplete and ongoing, put a ghost node at last. Set `pending` as truthy value to enable displaying pending item. You can customize the pending content by passing a React Element. Meanwhile, `slot="pendingDot"` is used to customize the dot of the pending item.
`reverse={true}` is used for reversing nodes.
</us>
```html
<template>
<a-timeline pending="Recording...">
<div>
<a-timeline pending="Recording..." :reverse="reverse">
<a-timeline-item>Create a services site 2015-09-01</a-timeline-item>
<a-timeline-item>Solve initial network problems 2015-09-01</a-timeline-item>
<a-timeline-item>Technical testing 2015-09-01</a-timeline-item>
</a-timeline>
<a-button type="primary" style="margin-top: 16px" @click="handleClick">Toggle Reverse</a-button>
</div>
</template>
<script>
export default {
data() {
return {
reverse: false,
}
},
methods: {
handleClick(){
this.reverse = !this.reverse
}
}
}
</script>
```

View File

@ -0,0 +1,25 @@
<cn>
#### 右侧时间轴点
时间轴点可以在内容的右边。
</cn>
<us>
#### Right alternate
Right alternate timeline.
</us>
```html
<template>
<a-timeline mode="right">
<a-timeline-item>Create a services site 2015-09-01</a-timeline-item>
<a-timeline-item>Solve initial network problems 2015-09-01</a-timeline-item>
<a-timeline-item>
<a-icon slot="dot" type="clock-circle-o" style="font-size: 16px;" />
Technical testing 2015-09-01
</a-timeline-item>
<a-timeline-item>Network problems being solved 2015-09-01</a-timeline-item>
</a-timeline>
</template>
```

View File

@ -17,6 +17,9 @@ Timeline
| -------- | ----------- | ---- | ------- |
| pending | Set the last ghost node's existence or its content | boolean\|string\|slot | `false` |
| pendingDot | Set the dot of the last ghost node when pending is true | string\|slot | `<Icon type="loading" />` |
| reverse | reverse nodes or not | boolean | false |
| mode | By sending `alternate` the timeline will distribute the nodes to the left and right. | `left` \| `alternate` \| `right` | `left` |
### Timeline.Item

View File

@ -17,6 +17,8 @@
| --- | --- | --- | --- |
| pending | 指定最后一个幽灵节点是否存在或内容 | boolean\|string\|slot | false |
| pendingDot | 当最后一个幽灵节点存在時,指定其时间图点 | string\|slot | `<Icon type="loading" />` |
| reverse | 节点排序 | boolean | false |
| mode | 通过设置 `mode` 可以改变时间轴和内容的相对位置 | `left` \| `alternate` \| `right` |
### Timeline.Item

View File

@ -37,7 +37,6 @@ function createBaseForm (option = {}, mixins = []) {
props = {},
templateContext,
} = option
return function decorate (WrappedComponent) {
let formProps = {}
if (Array.isArray(props)) {
@ -83,7 +82,7 @@ function createBaseForm (option = {}, mixins = []) {
submitting: false,
}
},
watch: {
watch: templateContext ? {} : {
'$props': {
handler: function (nextProps) {
if (mapPropsToFields) {
@ -105,6 +104,12 @@ function createBaseForm (option = {}, mixins = []) {
this.wrappedComponentRef(null)
},
methods: {
updateFields (fields = {}) {
this.fieldsStore.updateFields(mapPropsToFields(fields))
if (templateContext) {
templateContext.$forceUpdate()
}
},
onCollectCommon (name, action, args) {
const fieldMeta = this.fieldsStore.getFieldMeta(name)
if (fieldMeta[action]) {
@ -607,9 +612,11 @@ function createBaseForm (option = {}, mixins = []) {
on: $listeners,
ref: 'WrappedComponent',
}
return <WrappedComponent {...wrappedComponentProps}>{$slots.default}</WrappedComponent>
return WrappedComponent ? <WrappedComponent {...wrappedComponentProps}>{$slots.default}</WrappedComponent> : null
},
}
if (!WrappedComponent) return Form
if (Array.isArray(WrappedComponent.props)) {
const newProps = {}
WrappedComponent.props.forEach((prop) => {

View File

@ -92,6 +92,7 @@ export const SelectPropTypes = {
treeIcon: PropTypes.bool,
treeLine: PropTypes.bool,
treeDefaultExpandAll: PropTypes.bool,
treeDefaultExpandedKeys: PropTypes.arrayOf(String),
treeCheckable: PropTypes.any, // bool vnode
treeNodeLabelProp: PropTypes.string,
treeNodeFilterProp: PropTypes.string,

View File

@ -21,6 +21,16 @@ import {
* other props can pass with context for future refactor.
*/
function getWatch (keys = []) {
const watch = {}
keys.forEach(k => {
watch[k] = function () {
this.needSyncKeys[k] = true
}
})
return watch
}
const Tree = {
name: 'Tree',
mixins: [BaseMixin],
@ -91,6 +101,7 @@ const Tree = {
}),
data () {
this.needSyncKeys = {}
const state = {
_posEntities: {},
_keyEntities: {},
@ -124,8 +135,10 @@ const Tree = {
},
watch: {
...getWatch(['treeData', 'children', 'expandedKeys', 'autoExpandParent', 'selectedKeys', 'checkedKeys', 'loadedKeys']),
__propsSymbol__ () {
this.setState(this.getDerivedStateFromProps(getOptionProps(this), this.$data))
this.needSyncKeys = {}
},
},
@ -135,9 +148,9 @@ const Tree = {
const newState = {
_prevProps: { ...props },
}
const self = this
function needSync (name) {
return (!_prevProps && name in props) || (_prevProps && _prevProps[name] !== props[name])
return (!_prevProps && name in props) || (_prevProps && self.needSyncKeys[name])
}
// ================== Tree Node ==================

View File

@ -1,6 +1,6 @@
{
"name": "ant-design-vue",
"version": "1.1.9",
"version": "1.1.10",
"title": "Ant Design Vue",
"description": "An enterprise-class UI design language and Vue-based implementation",
"keywords": [
@ -181,7 +181,7 @@
"resize-observer-polyfill": "^1.5.0",
"shallow-equal": "^1.0.0",
"shallowequal": "^1.0.2",
"vue-ref": "^1.0.2",
"vue-ref": "^1.0.3",
"warning": "^3.0.0"
}
}

View File

@ -18,11 +18,13 @@
</style>
<script>
var _hmt = _hmt || [];
var isGithub = location.host.indexOf('github') !== -1;
var src = isGithub ? 'https://hm.baidu.com/hm.js?b3ef688fa86bfb75027f1b410180a867' : 'https://hm.baidu.com/hm.js?1564b57c5b8f74933e4fedca9dc75b0d';
(function() {
const hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?b3ef688fa86bfb75027f1b410180a867'
const s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
var hm = document.createElement('script');
hm.src = src;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})()
</script>
</head>
@ -50,4 +52,4 @@
</script>
</body>
</html>
</html>