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 arrayโ€™s 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 controls๏ผŒthe 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 controls๏ผŒthe 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>