Form表单 - 图1

Form 表单

具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。

如需要使用 v-model 双向绑定式的校验功能可使用新的表单 a-form-model

何时使用

  • 用于创建一个实体或收集信息。
  • 需要对输入的数据类型进行校验时。

表单

我们为 form 提供了以下三种排列方式:

  • 水平排列:标签和表单控件水平排列;(默认)
  • 垂直排列:标签和表单控件上下垂直排列;
  • 行内排列:表单项水平行内排列。

表单域

表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。

这里我们封装了表单域 <Form.Item />

注意:

1、如果使用 Form.create 处理表单使其具有自动收集数据并校验的功能,建议使用jsx
2、如果不是使用Vue.use(Form)形式注册的Form组件,你需要自行将$form挂载到Vue原型上。
Vue.prototype.$form = Form

代码演示

Note

Gender

Select a option and change input text above

Form表单 - 图2

Submit

表单联动

使用 setFieldsValue 来动态设置其他控件的值。

  1. <template>
  2. <a-form :form="form" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }" @submit="handleSubmit">
  3. <a-form-item label="Note">
  4. <a-input
  5. v-decorator="['note', { rules: [{ required: true, message: 'Please input your note!' }] }]"
  6. />
  7. </a-form-item>
  8. <a-form-item label="Gender">
  9. <a-select
  10. v-decorator="[
  11. 'gender',
  12. { rules: [{ required: true, message: 'Please select your gender!' }] },
  13. ]"
  14. placeholder="Select a option and change input text above"
  15. @change="handleSelectChange"
  16. >
  17. <a-select-option value="male">
  18. male
  19. </a-select-option>
  20. <a-select-option value="female">
  21. female
  22. </a-select-option>
  23. </a-select>
  24. </a-form-item>
  25. <a-form-item :wrapper-col="{ span: 12, offset: 5 }">
  26. <a-button type="primary" html-type="submit">
  27. Submit
  28. </a-button>
  29. </a-form-item>
  30. </a-form>
  31. </template>
  32. <script>
  33. export default {
  34. data() {
  35. return {
  36. formLayout: 'horizontal',
  37. form: this.$form.createForm(this, { name: 'coordinated' }),
  38. };
  39. },
  40. methods: {
  41. handleSubmit(e) {
  42. e.preventDefault();
  43. this.form.validateFields((err, values) => {
  44. if (!err) {
  45. console.log('Received values of form: ', values);
  46. }
  47. });
  48. },
  49. handleSelectChange(value) {
  50. console.log(value);
  51. this.form.setFieldsValue({
  52. note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
  53. });
  54. },
  55. },
  56. };
  57. </script>

Name

Nickname

Nickname is required

Check

动态校验规则

根据不同情况执行不同的校验规则。

  1. <template>
  2. <a-form :form="form">
  3. <a-form-item
  4. :label-col="formItemLayout.labelCol"
  5. :wrapper-col="formItemLayout.wrapperCol"
  6. label="Name"
  7. >
  8. <a-input
  9. v-decorator="[
  10. 'username',
  11. { rules: [{ required: true, message: 'Please input your name' }] },
  12. ]"
  13. placeholder="Please input your name"
  14. />
  15. </a-form-item>
  16. <a-form-item
  17. :label-col="formItemLayout.labelCol"
  18. :wrapper-col="formItemLayout.wrapperCol"
  19. label="Nickname"
  20. >
  21. <a-input
  22. v-decorator="[
  23. 'nickname',
  24. { rules: [{ required: checkNick, message: 'Please input your nickname' }] },
  25. ]"
  26. placeholder="Please input your nickname"
  27. />
  28. </a-form-item>
  29. <a-form-item :label-col="formTailLayout.labelCol" :wrapper-col="formTailLayout.wrapperCol">
  30. <a-checkbox :checked="checkNick" @change="handleChange">
  31. Nickname is required
  32. </a-checkbox>
  33. </a-form-item>
  34. <a-form-item :label-col="formTailLayout.labelCol" :wrapper-col="formTailLayout.wrapperCol">
  35. <a-button type="primary" @click="check">
  36. Check
  37. </a-button>
  38. </a-form-item>
  39. </a-form>
  40. </template>
  41. <script>
  42. const formItemLayout = {
  43. labelCol: { span: 4 },
  44. wrapperCol: { span: 8 },
  45. };
  46. const formTailLayout = {
  47. labelCol: { span: 4 },
  48. wrapperCol: { span: 8, offset: 4 },
  49. };
  50. export default {
  51. data() {
  52. return {
  53. checkNick: false,
  54. formItemLayout,
  55. formTailLayout,
  56. form: this.$form.createForm(this, { name: 'dynamic_rule' }),
  57. };
  58. },
  59. methods: {
  60. check() {
  61. this.form.validateFields(err => {
  62. if (!err) {
  63. console.info('success');
  64. }
  65. });
  66. },
  67. handleChange(e) {
  68. this.checkNick = e.target.checked;
  69. this.$nextTick(() => {
  70. this.form.validateFields(['nickname'], { force: true });
  71. });
  72. },
  73. },
  74. };
  75. </script>

Form表单 - 图3

Form表单 - 图4

Log in

内联登录栏

水平登录栏,常用在顶部导航栏中。

  1. <template>
  2. <a-form layout="inline" :form="form" @submit="handleSubmit">
  3. <a-form-item :validate-status="userNameError() ? 'error' : ''" :help="userNameError() || ''">
  4. <a-input
  5. v-decorator="[
  6. 'userName',
  7. { rules: [{ required: true, message: 'Please input your username!' }] },
  8. ]"
  9. placeholder="Username"
  10. >
  11. <a-icon slot="prefix" type="user" style="color:rgba(0,0,0,.25)" />
  12. </a-input>
  13. </a-form-item>
  14. <a-form-item :validate-status="passwordError() ? 'error' : ''" :help="passwordError() || ''">
  15. <a-input
  16. v-decorator="[
  17. 'password',
  18. { rules: [{ required: true, message: 'Please input your Password!' }] },
  19. ]"
  20. type="password"
  21. placeholder="Password"
  22. >
  23. <a-icon slot="prefix" type="lock" style="color:rgba(0,0,0,.25)" />
  24. </a-input>
  25. </a-form-item>
  26. <a-form-item>
  27. <a-button type="primary" html-type="submit" :disabled="hasErrors(form.getFieldsError())">
  28. Log in
  29. </a-button>
  30. </a-form-item>
  31. </a-form>
  32. </template>
  33. <script>
  34. function hasErrors(fieldsError) {
  35. return Object.keys(fieldsError).some(field => fieldsError[field]);
  36. }
  37. export default {
  38. data() {
  39. return {
  40. hasErrors,
  41. form: this.$form.createForm(this, { name: 'horizontal_login' }),
  42. };
  43. },
  44. mounted() {
  45. this.$nextTick(() => {
  46. // To disabled submit button at the beginning.
  47. this.form.validateFields();
  48. });
  49. },
  50. methods: {
  51. // Only show error after a field is touched.
  52. userNameError() {
  53. const { getFieldError, isFieldTouched } = this.form;
  54. return isFieldTouched('userName') && getFieldError('userName');
  55. },
  56. // Only show error after a field is touched.
  57. passwordError() {
  58. const { getFieldError, isFieldTouched } = this.form;
  59. return isFieldTouched('password') && getFieldError('password');
  60. },
  61. handleSubmit(e) {
  62. e.preventDefault();
  63. this.form.validateFields((err, values) => {
  64. if (!err) {
  65. console.log('Received values of form: ', values);
  66. }
  67. });
  68. },
  69. },
  70. };
  71. </script>

Form Layout

Horizontal Vertical Inline

Field A

Field B

Submit

表单布局

表单有三种布局。

  1. <template>
  2. <a-form :layout="formLayout">
  3. <a-form-item
  4. label="Form Layout"
  5. :label-col="formItemLayout.labelCol"
  6. :wrapper-col="formItemLayout.wrapperCol"
  7. >
  8. <a-radio-group default-value="horizontal" @change="handleFormLayoutChange">
  9. <a-radio-button value="horizontal">
  10. Horizontal
  11. </a-radio-button>
  12. <a-radio-button value="vertical">
  13. Vertical
  14. </a-radio-button>
  15. <a-radio-button value="inline">
  16. Inline
  17. </a-radio-button>
  18. </a-radio-group>
  19. </a-form-item>
  20. <a-form-item
  21. label="Field A"
  22. :label-col="formItemLayout.labelCol"
  23. :wrapper-col="formItemLayout.wrapperCol"
  24. >
  25. <a-input placeholder="input placeholder" />
  26. </a-form-item>
  27. <a-form-item
  28. label="Field B"
  29. :label-col="formItemLayout.labelCol"
  30. :wrapper-col="formItemLayout.wrapperCol"
  31. >
  32. <a-input placeholder="input placeholder" />
  33. </a-form-item>
  34. <a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
  35. <a-button type="primary">
  36. Submit
  37. </a-button>
  38. </a-form-item>
  39. </a-form>
  40. </template>
  41. <script>
  42. export default {
  43. data() {
  44. return {
  45. formLayout: 'horizontal',
  46. };
  47. },
  48. computed: {
  49. formItemLayout() {
  50. const { formLayout } = this;
  51. return formLayout === 'horizontal'
  52. ? {
  53. labelCol: { span: 4 },
  54. wrapperCol: { span: 14 },
  55. }
  56. : {};
  57. },
  58. buttonItemLayout() {
  59. const { formLayout } = this;
  60. return formLayout === 'horizontal'
  61. ? {
  62. wrapperCol: { span: 14, offset: 4 },
  63. }
  64. : {};
  65. },
  66. },
  67. methods: {
  68. handleFormLayoutChange(e) {
  69. this.formLayout = e.target.value;
  70. },
  71. },
  72. };
  73. </script>

Fail

Should be combination of numbers & alphabets

Warning

Validating

Form表单 - 图5

The information is being validated…

Success

Form表单 - 图6

Warning

Form表单 - 图7

Fail

Form表单 - 图8

Should be combination of numbers & alphabets

Success

Form表单 - 图9

Form表单 - 图10

Warning

Form表单 - 图11**Form表单 - 图12

Error

Option 1

Form表单 - 图13

Form表单 - 图14

Validating

Form表单 - 图15Form表单 - 图16Form表单 - 图17

The information is being validated…

inline

Form表单 - 图18

Please select the correct date

-

Form表单 - 图19

Success

Form表单 - 图20**Form表单 - 图21

Form表单 - 图22

Success

Form表单 - 图23

Warning

Form表单 - 图24**Form表单 - 图25

Error

Form表单 - 图26**Form表单 - 图27

自定义校验

我们提供了 validateStatus help hasFeedback 等属性,你可以不需要使用 Form.creategetFieldDecorator,自己定义校验的时机和内容。

  1. validateStatus: 校验状态,可选 ‘success’, ‘warning’, ‘error’, ‘validating’。
  2. hasFeedback:用于给输入框添加反馈图标。
  3. help:设置校验文案。
  1. <template>
  2. <a-form :label-col="labelCol" :wrapper-col="wrapperCol">
  3. <a-form-item
  4. label="Fail"
  5. validate-status="error"
  6. help="Should be combination of numbers & alphabets"
  7. >
  8. <a-input id="error" placeholder="unavailable choice" />
  9. </a-form-item>
  10. <a-form-item label="Warning" validate-status="warning">
  11. <a-input id="warning" placeholder="Warning" />
  12. </a-form-item>
  13. <a-form-item
  14. label="Validating"
  15. has-feedback
  16. validate-status="validating"
  17. help="The information is being validated..."
  18. >
  19. <a-input id="validating" placeholder="I'm the content is being validated" />
  20. </a-form-item>
  21. <a-form-item label="Success" has-feedback validate-status="success">
  22. <a-input id="success" placeholder="I'm the content" />
  23. </a-form-item>
  24. <a-form-item label="Warning" has-feedback validate-status="warning">
  25. <a-input id="warning2" placeholder="Warning" />
  26. </a-form-item>
  27. <a-form-item
  28. label="Fail"
  29. has-feedback
  30. validate-status="error"
  31. help="Should be combination of numbers & alphabets"
  32. >
  33. <a-input id="error2" placeholder="unavailable choice" />
  34. </a-form-item>
  35. <a-form-item label="Success" has-feedback validate-status="success">
  36. <a-date-picker style="width: 100%" />
  37. </a-form-item>
  38. <a-form-item label="Warning" has-feedback validate-status="warning">
  39. <a-time-picker style="width: 100%" />
  40. </a-form-item>
  41. <a-form-item label="Error" has-feedback validate-status="error">
  42. <a-select default-value="1">
  43. <a-select-option value="1">
  44. Option 1
  45. </a-select-option>
  46. <a-select-option value="2">
  47. Option 2
  48. </a-select-option>
  49. <a-select-option value="3">
  50. Option 3
  51. </a-select-option>
  52. </a-select>
  53. </a-form-item>
  54. <a-form-item
  55. label="Validating"
  56. has-feedback
  57. validate-status="validating"
  58. help="The information is being validated..."
  59. >
  60. <a-cascader :default-value="['1']" :options="[]" />
  61. </a-form-item>
  62. <a-form-item label="inline" style="margin-bottom:0;">
  63. <a-form-item
  64. validate-status="error"
  65. help="Please select the correct date"
  66. :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }"
  67. >
  68. <a-date-picker style="width: 100%" />
  69. </a-form-item>
  70. <span :style="{ display: 'inline-block', width: '24px', textAlign: 'center' }">
  71. -
  72. </span>
  73. <a-form-item :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }">
  74. <a-date-picker style="width: 100%" />
  75. </a-form-item>
  76. </a-form-item>
  77. <a-form-item label="Success" has-feedback validate-status="success">
  78. <a-input-number style="width: 100%" />
  79. </a-form-item>
  80. <a-form-item label="Success" has-feedback validate-status="success">
  81. <a-input allow-clear placeholder="with allowClear" />
  82. </a-form-item>
  83. <a-form-item label="Warning" has-feedback validate-status="warning">
  84. <a-input-password placeholder="with input password" />
  85. </a-form-item>
  86. <a-form-item label="Error" has-feedback validate-status="error">
  87. <a-input-password allow-clear placeholder="with input password and allowClear" />
  88. </a-form-item>
  89. </a-form>
  90. </template>
  91. <script>
  92. export default {
  93. data() {
  94. return {
  95. labelCol: {
  96. xs: { span: 24 },
  97. sm: { span: 5 },
  98. },
  99. wrapperCol: {
  100. xs: { span: 24 },
  101. sm: { span: 12 },
  102. },
  103. };
  104. },
  105. };
  106. </script>

Prime between 8 & 12

Form表单 - 图28**Form表单 - 图29

A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.

自行处理表单数据

使用 Form.create 处理后的表单具有自动收集数据并校验的功能,但如果您不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用 Form.create 并自行处理数据。

  1. <template>
  2. <a-form>
  3. <a-form-item
  4. :label-col="labelCol"
  5. :wrapper-col="wrapperCol"
  6. label="Prime between 8 & 12"
  7. :validate-status="number.validateStatus"
  8. :help="number.errorMsg || tips"
  9. >
  10. <a-input-number :min="8" :max="12" :value="number.value" @change="handleNumberChange" />
  11. </a-form-item>
  12. </a-form>
  13. </template>
  14. <script>
  15. function validatePrimeNumber(number) {
  16. if (number === 11) {
  17. return {
  18. validateStatus: 'success',
  19. errorMsg: null,
  20. };
  21. }
  22. return {
  23. validateStatus: 'error',
  24. errorMsg: 'The prime between 8 and 12 is 11!',
  25. };
  26. }
  27. export default {
  28. data() {
  29. return {
  30. labelCol: { span: 7 },
  31. wrapperCol: { span: 12 },
  32. number: {
  33. value: 11,
  34. },
  35. tips:
  36. 'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.',
  37. };
  38. },
  39. methods: {
  40. handleNumberChange(value) {
  41. this.number = {
  42. ...validatePrimeNumber(value),
  43. value,
  44. };
  45. },
  46. },
  47. };
  48. </script>

Form表单 - 图30

高级搜索

三列栅格式的表单排列方式,常用于数据表格的高级搜索。
有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。

  1. <template>
  2. <div id="components-form-demo-advanced-search">
  3. <a-form class="ant-advanced-search-form" :form="form" @submit="handleSearch">
  4. <a-row :gutter="24">
  5. <a-col
  6. v-for="i in 10"
  7. :key="i"
  8. :span="8"
  9. :style="{ display: i < count ? 'block' : 'none' }"
  10. >
  11. <a-form-item :label="`Field ${i}`">
  12. <a-input
  13. v-decorator="[
  14. `field-${i}`,
  15. {
  16. rules: [
  17. {
  18. required: true,
  19. message: 'Input something!',
  20. },
  21. ],
  22. },
  23. ]"
  24. placeholder="placeholder"
  25. />
  26. </a-form-item>
  27. </a-col>
  28. </a-row>
  29. <a-row>
  30. <a-col :span="24" :style="{ textAlign: 'right' }">
  31. <a-button type="primary" html-type="submit">
  32. Search
  33. </a-button>
  34. <a-button :style="{ marginLeft: '8px' }" @click="handleReset">
  35. Clear
  36. </a-button>
  37. <a :style="{ marginLeft: '8px', fontSize: '12px' }" @click="toggle">
  38. Collapse <a-icon :type="expand ? 'up' : 'down'" />
  39. </a>
  40. </a-col>
  41. </a-row>
  42. </a-form>
  43. <div class="search-result-list">
  44. Search Result List
  45. </div>
  46. </div>
  47. </template>
  48. <script>
  49. export default {
  50. data() {
  51. return {
  52. expand: false,
  53. form: this.$form.createForm(this, { name: 'advanced_search' }),
  54. };
  55. },
  56. computed: {
  57. count() {
  58. return this.expand ? 11 : 7;
  59. },
  60. },
  61. updated() {
  62. console.log('updated');
  63. },
  64. methods: {
  65. handleSearch(e) {
  66. e.preventDefault();
  67. this.form.validateFields((error, values) => {
  68. console.log('error', error);
  69. console.log('Received values of form: ', values);
  70. });
  71. },
  72. handleReset() {
  73. this.form.resetFields();
  74. },
  75. toggle() {
  76. this.expand = !this.expand;
  77. },
  78. },
  79. };
  80. </script>
  81. <style>
  82. .ant-advanced-search-form {
  83. padding: 24px;
  84. background: #fbfbfb;
  85. border: 1px solid #d9d9d9;
  86. border-radius: 6px;
  87. }
  88. .ant-advanced-search-form .ant-form-item {
  89. display: flex;
  90. }
  91. .ant-advanced-search-form .ant-form-item-control-wrapper {
  92. flex: 1;
  93. }
  94. #components-form-demo-advanced-search .ant-form {
  95. max-width: none;
  96. }
  97. #components-form-demo-advanced-search .search-result-list {
  98. margin-top: 16px;
  99. border: 1px dashed #e9e9e9;
  100. border-radius: 6px;
  101. background-color: #fafafa;
  102. min-height: 200px;
  103. text-align: center;
  104. padding-top: 80px;
  105. }
  106. </style>

Price

RMB

Form表单 - 图31

Submit

自定义表单控件

自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:

  • 提供受控属性 value 或其它与 valuePropName-参数) 的值同名的属性。
  • 提供 onChange 事件或 trigger-参数) 的值同名的事件。
  • 不能是函数式组件。
  1. <template>
  2. <a-form layout="inline" :form="form" @submit="handleSubmit">
  3. <a-form-item label="Price">
  4. <price-input
  5. v-decorator="[
  6. 'price',
  7. {
  8. initialValue: { number: 0, currency: 'rmb' },
  9. rules: [{ validator: checkPrice }],
  10. },
  11. ]"
  12. />
  13. </a-form-item>
  14. <a-form-item>
  15. <a-button type="primary" html-type="submit">
  16. Submit
  17. </a-button>
  18. </a-form-item>
  19. </a-form>
  20. </template>
  21. <script>
  22. const PriceInput = {
  23. props: ['value'],
  24. template: `
  25. <span>
  26. <a-input
  27. type='text'
  28. :value="number"
  29. @change="handleNumberChange"
  30. style="width: 63%; margin-right: 2%;"
  31. />
  32. <a-select
  33. :value="currency"
  34. style="width: 32%"
  35. @change="handleCurrencyChange"
  36. >
  37. <a-select-option value='rmb'>RMB</a-select-option>
  38. <a-select-option value='dollar'>Dollar</a-select-option>
  39. </a-select>
  40. </span>
  41. `,
  42. data() {
  43. const value = this.value || {};
  44. return {
  45. number: value.number || 0,
  46. currency: value.currency || 'rmb',
  47. };
  48. },
  49. watch: {
  50. value(val = {}) {
  51. this.number = val.number || 0;
  52. this.currency = val.currency || 'rmb';
  53. },
  54. },
  55. methods: {
  56. handleNumberChange(e) {
  57. const number = parseInt(e.target.value || 0, 10);
  58. if (isNaN(number)) {
  59. return;
  60. }
  61. this.triggerChange({ number });
  62. },
  63. handleCurrencyChange(currency) {
  64. this.triggerChange({ currency });
  65. },
  66. triggerChange(changedValue) {
  67. // Should provide an event to pass value to Form.
  68. this.$emit('change', Object.assign({}, this.$data, changedValue));
  69. },
  70. },
  71. };
  72. export default {
  73. components: {
  74. PriceInput,
  75. },
  76. beforeCreate() {
  77. this.form = this.$form.createForm(this, { name: 'customized_form_controls' });
  78. },
  79. methods: {
  80. handleSubmit(e) {
  81. e.preventDefault();
  82. this.form.validateFields((err, values) => {
  83. if (!err) {
  84. console.log('Received values of form: ', values);
  85. }
  86. });
  87. },
  88. checkPrice(rule, value, callback) {
  89. if (value.number > 0) {
  90. callback();
  91. return;
  92. }
  93. callback('Price must greater than zero!');
  94. },
  95. },
  96. };
  97. </script>

Form表单 - 图32Add field

Submit

动态增减表单项

动态增加、减少表单项。

  1. <template>
  2. <a-form :form="form" @submit="handleSubmit">
  3. <a-form-item
  4. v-for="(k, index) in form.getFieldValue('keys')"
  5. :key="k"
  6. v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
  7. :label="index === 0 ? 'Passengers' : ''"
  8. :required="false"
  9. >
  10. <a-input
  11. v-decorator="[
  12. `names[${k}]`,
  13. {
  14. validateTrigger: ['change', 'blur'],
  15. rules: [
  16. {
  17. required: true,
  18. whitespace: true,
  19. message: 'Please input passenger\'s name or delete this field.',
  20. },
  21. ],
  22. },
  23. ]"
  24. placeholder="passenger name"
  25. style="width: 60%; margin-right: 8px"
  26. />
  27. <a-icon
  28. v-if="form.getFieldValue('keys').length > 1"
  29. class="dynamic-delete-button"
  30. type="minus-circle-o"
  31. :disabled="form.getFieldValue('keys').length === 1"
  32. @click="() => remove(k)"
  33. />
  34. </a-form-item>
  35. <a-form-item v-bind="formItemLayoutWithOutLabel">
  36. <a-button type="dashed" style="width: 60%" @click="add">
  37. <a-icon type="plus" /> Add field
  38. </a-button>
  39. </a-form-item>
  40. <a-form-item v-bind="formItemLayoutWithOutLabel">
  41. <a-button type="primary" html-type="submit">
  42. Submit
  43. </a-button>
  44. </a-form-item>
  45. </a-form>
  46. </template>
  47. <script>
  48. let id = 0;
  49. export default {
  50. data() {
  51. return {
  52. formItemLayout: {
  53. labelCol: {
  54. xs: { span: 24 },
  55. sm: { span: 4 },
  56. },
  57. wrapperCol: {
  58. xs: { span: 24 },
  59. sm: { span: 20 },
  60. },
  61. },
  62. formItemLayoutWithOutLabel: {
  63. wrapperCol: {
  64. xs: { span: 24, offset: 0 },
  65. sm: { span: 20, offset: 4 },
  66. },
  67. },
  68. };
  69. },
  70. beforeCreate() {
  71. this.form = this.$form.createForm(this, { name: 'dynamic_form_item' });
  72. this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
  73. },
  74. methods: {
  75. remove(k) {
  76. const { form } = this;
  77. // can use data-binding to get
  78. const keys = form.getFieldValue('keys');
  79. // We need at least one passenger
  80. if (keys.length === 1) {
  81. return;
  82. }
  83. // can use data-binding to set
  84. form.setFieldsValue({
  85. keys: keys.filter(key => key !== k),
  86. });
  87. },
  88. add() {
  89. const { form } = this;
  90. // can use data-binding to get
  91. const keys = form.getFieldValue('keys');
  92. const nextKeys = keys.concat(id++);
  93. // can use data-binding to set
  94. // important! notify form to detect changes
  95. form.setFieldsValue({
  96. keys: nextKeys,
  97. });
  98. },
  99. handleSubmit(e) {
  100. e.preventDefault();
  101. this.form.validateFields((err, values) => {
  102. if (!err) {
  103. const { keys, names } = values;
  104. console.log('Received values of form: ', values);
  105. console.log(
  106. 'Merged values:',
  107. keys.map(key => names[key]),
  108. );
  109. }
  110. });
  111. },
  112. },
  113. };
  114. </script>
  115. <style>
  116. .dynamic-delete-button {
  117. cursor: pointer;
  118. position: relative;
  119. top: 4px;
  120. font-size: 24px;
  121. color: #999;
  122. transition: all 0.3s;
  123. }
  124. .dynamic-delete-button:hover {
  125. color: #777;
  126. }
  127. .dynamic-delete-button[disabled] {
  128. cursor: not-allowed;
  129. opacity: 0.5;
  130. }
  131. </style>

Form表单 - 图33

弹出层中的新建表单

当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。

  1. <template>
  2. <div>
  3. <a-button type="primary" @click="showModal">
  4. New Collection
  5. </a-button>
  6. <collection-create-form
  7. ref="collectionForm"
  8. :visible="visible"
  9. @cancel="handleCancel"
  10. @create="handleCreate"
  11. />
  12. </div>
  13. </template>
  14. <script>
  15. const CollectionCreateForm = {
  16. props: ['visible'],
  17. beforeCreate() {
  18. this.form = this.$form.createForm(this, { name: 'form_in_modal' });
  19. },
  20. template: `
  21. <a-modal
  22. :visible="visible"
  23. title='Create a new collection'
  24. okText='Create'
  25. @cancel="() => { $emit('cancel') }"
  26. @ok="() => { $emit('create') }"
  27. >
  28. <a-form layout='vertical' :form="form">
  29. <a-form-item label='Title'>
  30. <a-input
  31. v-decorator="[
  32. 'title',
  33. {
  34. rules: [{ required: true, message: 'Please input the title of collection!' }],
  35. }
  36. ]"
  37. />
  38. </a-form-item>
  39. <a-form-item label='Description'>
  40. <a-input
  41. type='textarea'
  42. v-decorator="['description']"
  43. />
  44. </a-form-item>
  45. <a-form-item class='collection-create-form_last-form-item'>
  46. <a-radio-group
  47. v-decorator="[
  48. 'modifier',
  49. {
  50. initialValue: 'private',
  51. }
  52. ]"
  53. >
  54. <a-radio value='public'>Public</a-radio>
  55. <a-radio value='private'>Private</a-radio>
  56. </a-radio-group>
  57. </a-form-item>
  58. </a-form>
  59. </a-modal>
  60. `,
  61. };
  62. export default {
  63. components: { CollectionCreateForm },
  64. data() {
  65. return {
  66. visible: false,
  67. };
  68. },
  69. methods: {
  70. showModal() {
  71. this.visible = true;
  72. },
  73. handleCancel() {
  74. this.visible = false;
  75. },
  76. handleCreate() {
  77. const form = this.$refs.collectionForm.form;
  78. form.validateFields((err, values) => {
  79. if (err) {
  80. return;
  81. }
  82. console.log('Received values of form: ', values);
  83. form.resetFields();
  84. this.visible = false;
  85. });
  86. },
  87. },
  88. };
  89. </script>

Form表单 - 图34

表单数据存储于上层组件

通过使用 onFieldsChangemapPropsToFields,可以把表单的数据存储到上层组件。
注意:
mapPropsToFields 里面返回的表单域数据必须使用 Form.createFormField 包装。
如果你使用Form.create,上层组件传递的属性,必须在Form.create({ props: ...})的props中声明。
如果使用this.$form.createForm,你可以使用任何数据,不仅仅局限于上层组件的属性。

  1. <template>
  2. <div id="components-form-demo-global-state">
  3. <customized-form :username="fields.username" @change="handleFormChange" />
  4. <pre class="language-bash">
  5. {{ JSON.stringify(fields, null, 2) }}
  6. </pre>
  7. </div>
  8. </template>
  9. <script>
  10. const CustomizedForm = {
  11. props: ['username'],
  12. template: `
  13. <a-form layout='inline' :form="form">
  14. <a-form-item label='Username'>
  15. <a-input
  16. v-decorator="[
  17. 'username',
  18. {
  19. rules: [{ required: true, message: 'Username is required!' }],
  20. }
  21. ]"
  22. />
  23. </a-form-item>
  24. </a-form>
  25. `,
  26. created() {
  27. this.form = this.$form.createForm(this, {
  28. name: 'global_state',
  29. onFieldsChange: (_, changedFields) => {
  30. this.$emit('change', changedFields);
  31. },
  32. mapPropsToFields: () => {
  33. return {
  34. username: this.$form.createFormField({
  35. ...this.username,
  36. value: this.username.value,
  37. }),
  38. };
  39. },
  40. onValuesChange(_, values) {
  41. console.log(values);
  42. },
  43. });
  44. },
  45. watch: {
  46. username() {
  47. this.form.updateFields({
  48. username: this.$form.createFormField({
  49. ...this.username,
  50. value: this.username.value,
  51. }),
  52. });
  53. },
  54. },
  55. };
  56. export default {
  57. components: {
  58. CustomizedForm,
  59. },
  60. data() {
  61. return {
  62. fields: {
  63. username: {
  64. value: 'benjycui',
  65. },
  66. },
  67. };
  68. },
  69. methods: {
  70. handleFormChange(changedFields) {
  71. console.log('changedFields', changedFields);
  72. this.fields = { ...this.fields, ...changedFields };
  73. },
  74. },
  75. };
  76. </script>
  77. <style>
  78. #components-form-demo-global-state .language-bash {
  79. max-width: 400px;
  80. border-radius: 6px;
  81. margin-top: 24px;
  82. }
  83. </style>

Form表单 - 图35

表单数据存储于 Vuex Store 中

通过使用 onFieldsChange 与 mapPropsToFields,可以把表单的数据存储到 Vuex 中。
注意:
mapPropsToFields 里面返回的表单域数据必须使用 Form.createFormField 包装。

  1. <template>
  2. <div id="components-form-demo-vuex">
  3. <a-form :form="form" @submit="handleSubmit">
  4. <a-form-item label="Username">
  5. <a-input
  6. v-decorator="[
  7. 'username',
  8. {
  9. rules: [{ required: true, message: 'Username is required!' }],
  10. },
  11. ]"
  12. />
  13. </a-form-item>
  14. <a-button type="primary" html-type="submit">
  15. Submit
  16. </a-button>
  17. </a-form>
  18. </div>
  19. </template>
  20. <script>
  21. export default {
  22. computed: {
  23. username() {
  24. return this.$store.state.username;
  25. },
  26. },
  27. watch: {
  28. username(val) {
  29. console.log('this.$store.state.username: ', val);
  30. this.form.setFieldsValue({ username: val });
  31. },
  32. },
  33. created() {
  34. this.form = this.$form.createForm(this, {
  35. onFieldsChange: (_, changedFields) => {
  36. this.$emit('change', changedFields);
  37. },
  38. mapPropsToFields: () => {
  39. return {
  40. username: this.$form.createFormField({
  41. value: this.username,
  42. }),
  43. };
  44. },
  45. onValuesChange: (_, values) => {
  46. console.log(values);
  47. // Synchronize to vuex store in real time
  48. // this.$store.commit('update', values)
  49. },
  50. });
  51. },
  52. methods: {
  53. handleSubmit(e) {
  54. e.preventDefault();
  55. this.form.validateFields((err, values) => {
  56. if (!err) {
  57. console.log('Received values of form: ', values);
  58. this.$store.commit('update', values);
  59. }
  60. });
  61. },
  62. },
  63. };
  64. </script>
  65. <style>
  66. #components-form-demo-vuex .language-bash {
  67. max-width: 400px;
  68. border-radius: 6px;
  69. margin-top: 24px;
  70. }
  71. </style>

Form表单 - 图36

Form表单 - 图37

Remember meForgot password Log in Or register now!

登录框

普通的登录框,可以容纳更多的元素。

  1. <template>
  2. <a-form
  3. id="components-form-demo-normal-login"
  4. :form="form"
  5. class="login-form"
  6. @submit="handleSubmit"
  7. >
  8. <a-form-item>
  9. <a-input
  10. v-decorator="[
  11. 'userName',
  12. { rules: [{ required: true, message: 'Please input your username!' }] },
  13. ]"
  14. placeholder="Username"
  15. >
  16. <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
  17. </a-input>
  18. </a-form-item>
  19. <a-form-item>
  20. <a-input
  21. v-decorator="[
  22. 'password',
  23. { rules: [{ required: true, message: 'Please input your Password!' }] },
  24. ]"
  25. type="password"
  26. placeholder="Password"
  27. >
  28. <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
  29. </a-input>
  30. </a-form-item>
  31. <a-form-item>
  32. <a-checkbox
  33. v-decorator="[
  34. 'remember',
  35. {
  36. valuePropName: 'checked',
  37. initialValue: true,
  38. },
  39. ]"
  40. >
  41. Remember me
  42. </a-checkbox>
  43. <a class="login-form-forgot" href="">
  44. Forgot password
  45. </a>
  46. <a-button type="primary" html-type="submit" class="login-form-button">
  47. Log in
  48. </a-button>
  49. Or
  50. <a href="">
  51. register now!
  52. </a>
  53. </a-form-item>
  54. </a-form>
  55. </template>
  56. <script>
  57. export default {
  58. beforeCreate() {
  59. this.form = this.$form.createForm(this, { name: 'normal_login' });
  60. },
  61. methods: {
  62. handleSubmit(e) {
  63. e.preventDefault();
  64. this.form.validateFields((err, values) => {
  65. if (!err) {
  66. console.log('Received values of form: ', values);
  67. }
  68. });
  69. },
  70. },
  71. };
  72. </script>
  73. <style>
  74. #components-form-demo-normal-login .login-form {
  75. max-width: 300px;
  76. }
  77. #components-form-demo-normal-login .login-form-forgot {
  78. float: right;
  79. }
  80. #components-form-demo-normal-login .login-form-button {
  81. width: 100%;
  82. }
  83. </style>

E-mail

Password

Confirm Password

Nickname Form表单 - 图38

Habitual Residence

Zhejiang / Hangzhou / West LakeForm表单 - 图39**Form表单 - 图40

Phone Number

+86

Form表单 - 图41

Website

Form表单 - 图42

Captcha

Get captcha

We must make sure that your are a human.

I have read the agreement

Register

注册新用户

用户填写必须的信息以注册新用户。

  1. <template>
  2. <a-form :form="form" @submit="handleSubmit">
  3. <a-form-item v-bind="formItemLayout" label="E-mail">
  4. <a-input
  5. v-decorator="[
  6. 'email',
  7. {
  8. rules: [
  9. {
  10. type: 'email',
  11. message: 'The input is not valid E-mail!',
  12. },
  13. {
  14. required: true,
  15. message: 'Please input your E-mail!',
  16. },
  17. ],
  18. },
  19. ]"
  20. />
  21. </a-form-item>
  22. <a-form-item v-bind="formItemLayout" label="Password" has-feedback>
  23. <a-input
  24. v-decorator="[
  25. 'password',
  26. {
  27. rules: [
  28. {
  29. required: true,
  30. message: 'Please input your password!',
  31. },
  32. {
  33. validator: validateToNextPassword,
  34. },
  35. ],
  36. },
  37. ]"
  38. type="password"
  39. />
  40. </a-form-item>
  41. <a-form-item v-bind="formItemLayout" label="Confirm Password" has-feedback>
  42. <a-input
  43. v-decorator="[
  44. 'confirm',
  45. {
  46. rules: [
  47. {
  48. required: true,
  49. message: 'Please confirm your password!',
  50. },
  51. {
  52. validator: compareToFirstPassword,
  53. },
  54. ],
  55. },
  56. ]"
  57. type="password"
  58. @blur="handleConfirmBlur"
  59. />
  60. </a-form-item>
  61. <a-form-item v-bind="formItemLayout">
  62. <span slot="label">
  63. Nickname&nbsp;
  64. <a-tooltip title="What do you want others to call you?">
  65. <a-icon type="question-circle-o" />
  66. </a-tooltip>
  67. </span>
  68. <a-input
  69. v-decorator="[
  70. 'nickname',
  71. {
  72. rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
  73. },
  74. ]"
  75. />
  76. </a-form-item>
  77. <a-form-item v-bind="formItemLayout" label="Habitual Residence">
  78. <a-cascader
  79. v-decorator="[
  80. 'residence',
  81. {
  82. initialValue: ['zhejiang', 'hangzhou', 'xihu'],
  83. rules: [
  84. { type: 'array', required: true, message: 'Please select your habitual residence!' },
  85. ],
  86. },
  87. ]"
  88. :options="residences"
  89. />
  90. </a-form-item>
  91. <a-form-item v-bind="formItemLayout" label="Phone Number">
  92. <a-input
  93. v-decorator="[
  94. 'phone',
  95. {
  96. rules: [{ required: true, message: 'Please input your phone number!' }],
  97. },
  98. ]"
  99. style="width: 100%"
  100. >
  101. <a-select
  102. slot="addonBefore"
  103. v-decorator="['prefix', { initialValue: '86' }]"
  104. style="width: 70px"
  105. >
  106. <a-select-option value="86">
  107. +86
  108. </a-select-option>
  109. <a-select-option value="87">
  110. +87
  111. </a-select-option>
  112. </a-select>
  113. </a-input>
  114. </a-form-item>
  115. <a-form-item v-bind="formItemLayout" label="Website">
  116. <a-auto-complete
  117. v-decorator="['website', { rules: [{ required: true, message: 'Please input website!' }] }]"
  118. placeholder="website"
  119. @change="handleWebsiteChange"
  120. >
  121. <template slot="dataSource">
  122. <a-select-option v-for="website in autoCompleteResult" :key="website">
  123. {{ website }}
  124. </a-select-option>
  125. </template>
  126. <a-input />
  127. </a-auto-complete>
  128. </a-form-item>
  129. <a-form-item
  130. v-bind="formItemLayout"
  131. label="Captcha"
  132. extra="We must make sure that your are a human."
  133. >
  134. <a-row :gutter="8">
  135. <a-col :span="12">
  136. <a-input
  137. v-decorator="[
  138. 'captcha',
  139. { rules: [{ required: true, message: 'Please input the captcha you got!' }] },
  140. ]"
  141. />
  142. </a-col>
  143. <a-col :span="12">
  144. <a-button>Get captcha</a-button>
  145. </a-col>
  146. </a-row>
  147. </a-form-item>
  148. <a-form-item v-bind="tailFormItemLayout">
  149. <a-checkbox v-decorator="['agreement', { valuePropName: 'checked' }]">
  150. I have read the
  151. <a href="">
  152. agreement
  153. </a>
  154. </a-checkbox>
  155. </a-form-item>
  156. <a-form-item v-bind="tailFormItemLayout">
  157. <a-button type="primary" html-type="submit">
  158. Register
  159. </a-button>
  160. </a-form-item>
  161. </a-form>
  162. </template>
  163. <script>
  164. const residences = [
  165. {
  166. value: 'zhejiang',
  167. label: 'Zhejiang',
  168. children: [
  169. {
  170. value: 'hangzhou',
  171. label: 'Hangzhou',
  172. children: [
  173. {
  174. value: 'xihu',
  175. label: 'West Lake',
  176. },
  177. ],
  178. },
  179. ],
  180. },
  181. {
  182. value: 'jiangsu',
  183. label: 'Jiangsu',
  184. children: [
  185. {
  186. value: 'nanjing',
  187. label: 'Nanjing',
  188. children: [
  189. {
  190. value: 'zhonghuamen',
  191. label: 'Zhong Hua Men',
  192. },
  193. ],
  194. },
  195. ],
  196. },
  197. ];
  198. export default {
  199. data() {
  200. return {
  201. confirmDirty: false,
  202. residences,
  203. autoCompleteResult: [],
  204. formItemLayout: {
  205. labelCol: {
  206. xs: { span: 24 },
  207. sm: { span: 8 },
  208. },
  209. wrapperCol: {
  210. xs: { span: 24 },
  211. sm: { span: 16 },
  212. },
  213. },
  214. tailFormItemLayout: {
  215. wrapperCol: {
  216. xs: {
  217. span: 24,
  218. offset: 0,
  219. },
  220. sm: {
  221. span: 16,
  222. offset: 8,
  223. },
  224. },
  225. },
  226. };
  227. },
  228. beforeCreate() {
  229. this.form = this.$form.createForm(this, { name: 'register' });
  230. },
  231. methods: {
  232. handleSubmit(e) {
  233. e.preventDefault();
  234. this.form.validateFieldsAndScroll((err, values) => {
  235. if (!err) {
  236. console.log('Received values of form: ', values);
  237. }
  238. });
  239. },
  240. handleConfirmBlur(e) {
  241. const value = e.target.value;
  242. this.confirmDirty = this.confirmDirty || !!value;
  243. },
  244. compareToFirstPassword(rule, value, callback) {
  245. const form = this.form;
  246. if (value && value !== form.getFieldValue('password')) {
  247. callback('Two passwords that you enter is inconsistent!');
  248. } else {
  249. callback();
  250. }
  251. },
  252. validateToNextPassword(rule, value, callback) {
  253. const form = this.form;
  254. if (value && this.confirmDirty) {
  255. form.validateFields(['confirm'], { force: true });
  256. }
  257. callback();
  258. },
  259. handleWebsiteChange(value) {
  260. let autoCompleteResult;
  261. if (!value) {
  262. autoCompleteResult = [];
  263. } else {
  264. autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
  265. }
  266. this.autoCompleteResult = autoCompleteResult;
  267. },
  268. },
  269. };
  270. </script>

DatePicker

Form表单 - 图43

DatePicker[showTime]

Form表单 - 图44

MonthPicker

Form表单 - 图45

RangePicker

~

RangePicker[showTime]

~

TimePicker

Form表单 - 图46

Submit

时间类控件

时间类组件的 value 类型为 moment 对象,所以在提交服务器前需要预处理。

  1. <template>
  2. <a-form v-bind="formItemLayout" :form="form" @submit="handleSubmit">
  3. <a-form-item label="DatePicker">
  4. <a-date-picker v-decorator="['date-picker', config]" />
  5. </a-form-item>
  6. <a-form-item label="DatePicker[showTime]">
  7. <a-date-picker
  8. v-decorator="['date-time-picker', config]"
  9. show-time
  10. format="YYYY-MM-DD HH:mm:ss"
  11. />
  12. </a-form-item>
  13. <a-form-item label="MonthPicker">
  14. <a-month-picker v-decorator="['month-picker', config]" />
  15. </a-form-item>
  16. <a-form-item label="RangePicker">
  17. <a-range-picker v-decorator="['range-picker', rangeConfig]" />
  18. </a-form-item>
  19. <a-form-item label="RangePicker[showTime]">
  20. <a-range-picker
  21. v-decorator="['range-time-picker', rangeConfig]"
  22. show-time
  23. format="YYYY-MM-DD HH:mm:ss"
  24. />
  25. </a-form-item>
  26. <a-form-item label="TimePicker">
  27. <a-time-picker v-decorator="['time-picker', config]" />
  28. </a-form-item>
  29. <a-form-item
  30. :wrapper-col="{
  31. xs: { span: 24, offset: 0 },
  32. sm: { span: 16, offset: 8 },
  33. }"
  34. >
  35. <a-button type="primary" html-type="submit">
  36. Submit
  37. </a-button>
  38. </a-form-item>
  39. </a-form>
  40. </template>
  41. <script>
  42. export default {
  43. data() {
  44. return {
  45. formItemLayout: {
  46. labelCol: {
  47. xs: { span: 24 },
  48. sm: { span: 8 },
  49. },
  50. wrapperCol: {
  51. xs: { span: 24 },
  52. sm: { span: 16 },
  53. },
  54. },
  55. config: {
  56. rules: [{ type: 'object', required: true, message: 'Please select time!' }],
  57. },
  58. rangeConfig: {
  59. rules: [{ type: 'array', required: true, message: 'Please select time!' }],
  60. },
  61. };
  62. },
  63. beforeCreate() {
  64. this.form = this.$form.createForm(this, { name: 'time_related_controls' });
  65. },
  66. methods: {
  67. handleSubmit(e) {
  68. e.preventDefault();
  69. this.form.validateFields((err, fieldsValue) => {
  70. if (err) {
  71. return;
  72. }
  73. // Should format date value before submit.
  74. const rangeValue = fieldsValue['range-picker'];
  75. const rangeTimeValue = fieldsValue['range-time-picker'];
  76. const values = {
  77. ...fieldsValue,
  78. 'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
  79. 'date-time-picker': fieldsValue['date-time-picker'].format('YYYY-MM-DD HH:mm:ss'),
  80. 'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
  81. 'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
  82. 'range-time-picker': [
  83. rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss'),
  84. rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss'),
  85. ],
  86. 'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
  87. };
  88. console.log('Received values of form: ', values);
  89. });
  90. },
  91. },
  92. };
  93. </script>

Plain Text

China

Select

Please select a country

Form表单 - 图47

Select[multiple]

Please select favourite colors

  • InputNumber

Form表单 - 图48**Form表单 - 图49

machines

Switch

Slider

ABCDEF

Radio.Group

item 1 item 2 item 3

Radio.Button

item 1 item 2 item 3

Checkbox.Group

A

B

C

D

E

Rate

  • Form表单 - 图50

    Form表单 - 图51

  • Form表单 - 图52

    Form表单 - 图53

  • Form表单 - 图54

    Form表单 - 图55

  • Form表单 - 图56

    Form表单 - 图57

  • Form表单 - 图58

    Form表单 - 图59

Upload

Form表单 - 图60Click to upload

longgggggggggggggggggggggggggggggggggg

Dragger

Form表单 - 图61

Click or drag file to this area to upload

Support for a single or bulk upload.

Submit

校验其他组件

以上演示没有出现的表单控件对应的校验演示。

  1. <template>
  2. <a-form
  3. id="components-form-demo-validate-other"
  4. :form="form"
  5. v-bind="formItemLayout"
  6. @submit="handleSubmit"
  7. >
  8. <a-form-item label="Plain Text">
  9. <span class="ant-form-text">
  10. China
  11. </span>
  12. </a-form-item>
  13. <a-form-item label="Select" has-feedback>
  14. <a-select
  15. v-decorator="[
  16. 'select',
  17. { rules: [{ required: true, message: 'Please select your country!' }] },
  18. ]"
  19. placeholder="Please select a country"
  20. >
  21. <a-select-option value="china">
  22. China
  23. </a-select-option>
  24. <a-select-option value="usa">
  25. U.S.A
  26. </a-select-option>
  27. </a-select>
  28. </a-form-item>
  29. <a-form-item label="Select[multiple]">
  30. <a-select
  31. v-decorator="[
  32. 'select-multiple',
  33. {
  34. rules: [
  35. { required: true, message: 'Please select your favourite colors!', type: 'array' },
  36. ],
  37. },
  38. ]"
  39. mode="multiple"
  40. placeholder="Please select favourite colors"
  41. >
  42. <a-select-option value="red">
  43. Red
  44. </a-select-option>
  45. <a-select-option value="green">
  46. Green
  47. </a-select-option>
  48. <a-select-option value="blue">
  49. Blue
  50. </a-select-option>
  51. </a-select>
  52. </a-form-item>
  53. <a-form-item label="InputNumber">
  54. <a-input-number v-decorator="['input-number', { initialValue: 3 }]" :min="1" :max="10" />
  55. <span class="ant-form-text">
  56. machines
  57. </span>
  58. </a-form-item>
  59. <a-form-item label="Switch">
  60. <a-switch v-decorator="['switch', { valuePropName: 'checked' }]" />
  61. </a-form-item>
  62. <a-form-item label="Slider">
  63. <a-slider
  64. v-decorator="['slider']"
  65. :marks="{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }"
  66. />
  67. </a-form-item>
  68. <a-form-item label="Radio.Group">
  69. <a-radio-group v-decorator="['radio-group']">
  70. <a-radio value="a">
  71. item 1
  72. </a-radio>
  73. <a-radio value="b">
  74. item 2
  75. </a-radio>
  76. <a-radio value="c">
  77. item 3
  78. </a-radio>
  79. </a-radio-group>
  80. </a-form-item>
  81. <a-form-item label="Radio.Button">
  82. <a-radio-group v-decorator="['radio-button']">
  83. <a-radio-button value="a">
  84. item 1
  85. </a-radio-button>
  86. <a-radio-button value="b">
  87. item 2
  88. </a-radio-button>
  89. <a-radio-button value="c">
  90. item 3
  91. </a-radio-button>
  92. </a-radio-group>
  93. </a-form-item>
  94. <a-form-item label="Checkbox.Group">
  95. <a-checkbox-group
  96. v-decorator="['checkbox-group', { initialValue: ['A', 'B'] }]"
  97. style="width: 100%;"
  98. >
  99. <a-row>
  100. <a-col :span="8">
  101. <a-checkbox value="A">
  102. A
  103. </a-checkbox>
  104. </a-col>
  105. <a-col :span="8">
  106. <a-checkbox disabled value="B">
  107. B
  108. </a-checkbox>
  109. </a-col>
  110. <a-col :span="8">
  111. <a-checkbox value="C">
  112. C
  113. </a-checkbox>
  114. </a-col>
  115. <a-col :span="8">
  116. <a-checkbox value="D">
  117. D
  118. </a-checkbox>
  119. </a-col>
  120. <a-col :span="8">
  121. <a-checkbox value="E">
  122. E
  123. </a-checkbox>
  124. </a-col>
  125. </a-row>
  126. </a-checkbox-group>
  127. </a-form-item>
  128. <a-form-item label="Rate">
  129. <a-rate v-decorator="['rate', { initialValue: 3.5 }]" allow-half />
  130. </a-form-item>
  131. <a-form-item label="Upload" extra="longgggggggggggggggggggggggggggggggggg">
  132. <a-upload
  133. v-decorator="[
  134. 'upload',
  135. {
  136. valuePropName: 'fileList',
  137. getValueFromEvent: normFile,
  138. },
  139. ]"
  140. name="logo"
  141. action="/upload.do"
  142. list-type="picture"
  143. >
  144. <a-button> <a-icon type="upload" /> Click to upload </a-button>
  145. </a-upload>
  146. </a-form-item>
  147. <a-form-item label="Dragger">
  148. <div class="dropbox">
  149. <a-upload-dragger
  150. v-decorator="[
  151. 'dragger',
  152. {
  153. valuePropName: 'fileList',
  154. getValueFromEvent: normFile,
  155. },
  156. ]"
  157. name="files"
  158. action="/upload.do"
  159. >
  160. <p class="ant-upload-drag-icon">
  161. <a-icon type="inbox" />
  162. </p>
  163. <p class="ant-upload-text">
  164. Click or drag file to this area to upload
  165. </p>
  166. <p class="ant-upload-hint">
  167. Support for a single or bulk upload.
  168. </p>
  169. </a-upload-dragger>
  170. </div>
  171. </a-form-item>
  172. <a-form-item :wrapper-col="{ span: 12, offset: 6 }">
  173. <a-button type="primary" html-type="submit">
  174. Submit
  175. </a-button>
  176. </a-form-item>
  177. </a-form>
  178. </template>
  179. <script>
  180. export default {
  181. data: () => ({
  182. formItemLayout: {
  183. labelCol: { span: 6 },
  184. wrapperCol: { span: 14 },
  185. },
  186. }),
  187. beforeCreate() {
  188. this.form = this.$form.createForm(this, { name: 'validate_other' });
  189. },
  190. methods: {
  191. handleSubmit(e) {
  192. e.preventDefault();
  193. this.form.validateFields((err, values) => {
  194. if (!err) {
  195. console.log('Received values of form: ', values);
  196. }
  197. });
  198. },
  199. normFile(e) {
  200. console.log('Upload event:', e);
  201. if (Array.isArray(e)) {
  202. return e;
  203. }
  204. return e && e.fileList;
  205. },
  206. },
  207. };
  208. </script>
  209. <style>
  210. #components-form-demo-validate-other .dropbox {
  211. height: 180px;
  212. line-height: 1.5;
  213. }
  214. </style>

API

Form

参数说明类型默认值版本
formForm.create() 包装过的组件会自带 this.form 属性,如果使用 template 语法,可以使用 this.$form.createForm(this, options)object
hideRequiredMark隐藏所有表单项的必选标记Booleanfalse
labelAlignlabel 标签的文本对齐方式‘left’ | ‘right’‘right’1.5.0
layout表单布局‘horizontal’|’vertical’|’inline’‘horizontal’
labelCollabel 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12}sm: {span: 3, offset: 12}object
wrapperCol需要为输入控件设置布局样式时,使用该属性,用法同 labelColobject
selfUpdate自定义字段更新逻辑,说明见下,需 1.3.17 版本以上booleanfalse1.3.17
colon配置 Form.Item 的 colon 的默认值 (只有在属性 layout 为 horizontal 时有效)booleantrue1.5.0

事件

事件名称说明回调参数
submit数据验证成功后回调事件Function(e:Event)

Form.create(options) | this.$form.createForm(this, options)

使用方式如下:

jsx 使用方式,使用方式和 React 版 antd 一致

  1. const CustomizedForm = {};
  2. CustomizedForm = Form.create({})(CustomizedForm);

如果需要为包装组件实例维护一个 ref,可以使用wrappedComponentRef

单文件 template 使用方式

  1. <template>
  2. <a-form :form="form" />
  3. </template>
  4. <script>
  5. export default {
  6. beforeCreate() {
  7. this.form = this.$form.createForm(this, options);
  8. },
  9. };
  10. </script>

options 的配置项如下。

参数说明类型
props仅仅支持 Form.create({})(CustomizedForm)的使用方式,父组件需要映射到表单项上的属性声明(和vue 组件 props 一致){}
mapPropsToFields把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 Form.createFormField 标记,如果使用$form.createForm 创建收集器,你可以将任何数据映射到 Field 中,不受父组件约束(props) => ({ [fieldName]: FormField { value } })
name设置表单域内字段 id 的前缀-
validateMessages默认校验信息,可用于把默认错误信息改为中文等,格式与 newMessages 返回值一致Object { [nested.path]: String }
onFieldsChangeForm.Item 子节点的值发生改变时触发,可以把对应的值转存到 Redux storeFunction(props, fields)
onValuesChange任一表单域的值发生改变时的回调(props, values) => void

经过 Form.create 包装的组件将会自带 this.form 属性,this.form 提供的 API 如下:

注意:使用 getFieldsValue getFieldValue setFieldsValue 等时,应确保对应的 field 已经用 getFieldDecoratorv-decorator 注册过了。

方法说明类型
getFieldDecorator用于和表单进行双向绑定,单文件 template 可以使用指令v-decorator进行绑定,详见下方描述
getFieldError获取某个输入控件的 ErrorFunction(name)
getFieldsError获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 ErrorFunction([names: string[]])
getFieldsValue获取一组输入控件的值,如不传入参数,则获取全部组件的值Function([fieldNames: string[]])
getFieldValue获取一个输入控件的值Function(fieldName: string)
isFieldsTouched判断是否任一输入控件经历过 getFieldDecoratorv-decorator 的值收集时机 options.trigger(names?: string[]) => boolean
isFieldTouched判断一个输入控件是否经历过 getFieldDecoratorv-decorator 的值收集时机 options.trigger(name: string) => boolean
isFieldValidating判断一个输入控件是否在校验状态Function(name)
resetFields重置一组输入控件的值(为 initialValue)与状态,如不传入参数,则重置所有组件Function([names: string[]])
setFields设置一组输入控件的值与错误状态。Function({ [fieldName]: { value: any, errors: [Error] } })
setFieldsValue设置一组输入控件的值Function({ [fieldName]: value })
validateFields校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件Function([fieldNames: string[]], [options: object], callback: Function(errors, values))
validateFieldsAndScrollvalidateFields 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围参考 validateFields

validateFields/validateFieldsAndScroll

  1. const {
  2. form: { validateFields },
  3. } = this;
  4. validateFields((errors, values) => {
  5. // ...
  6. });
  7. validateFields(['field1', 'field2'], (errors, values) => {
  8. // ...
  9. });
  10. validateFields(['field1', 'field2'], options, (errors, values) => {
  11. // ...
  12. });
参数说明类型默认值
options.first若为 true,则每一表单域的都会在碰到第一个失败了的校验规则后停止校验booleanfalse
options.firstFields指定表单域会在碰到第一个失败了的校验规则后停止校验String[][]
options.force对已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验booleanfalse
options.scroll定义 validateFieldsAndScroll 的滚动行为,详细配置见 dom-scroll-into-view configObject{}

validateFields 的 callback 参数示例

  • errors:

    1. {
    2. "userName": {
    3. "errors": [
    4. {
    5. "message": "Please input your username!",
    6. "field": "userName"
    7. }
    8. ]
    9. },
    10. "password": {
    11. "errors": [
    12. {
    13. "message": "Please input your Password!",
    14. "field": "password"
    15. }
    16. ]
    17. }
    18. }
  • values:

    1. {
    2. "userName": "username",
    3. "password": "password",
    4. }

Form.createFormField

用于标记 mapPropsToFields 返回的表单域数据,例子

this.form.getFieldDecorator(id, options) 和 v-decorator=”[id, options]“

经过 getFieldDecoratorv-decorator 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:

  1. 不再需要也不应该onChange 来做同步,但还是可以继续监听 onChange 等事件。
  2. 你不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用 getFieldDecoratorv-decorator 里的 initialValue
  3. 你不应该用 v-model,可以使用 this.form.setFieldsValue 来动态改变表单值。

特别注意

  1. getFieldDecoratorv-decorator 不能用于装饰纯函数组件。
  2. getFieldDecoratorv-decorator 调用不能位于纯函数组件中 https://cn.vuejs.org/v2/api/#functional

getFieldDecorator(id, options) 和 v-decorator=”[id, options]“ 参数

参数说明类型默认值
id必填输入控件唯一标志。支持嵌套式的写法。string
options.getValueFromEvent可以把 onChange 的参数(如 event)转化为控件的值function(..args)reference
options.initialValue子节点的初始值,类型、可选值均由子节点决定(注意:由于内部校验时使用 === 判断是否变化,建议使用变量缓存所需设置的值而非直接使用字面量)
options.normalize转换默认的 value 给控件,一个选择全部的例子function(value, prevValue, allValues): any-
options.preserve即便字段不再使用,也保留该字段的值booleanfalse
options.rules校验规则,参考下方文档object[]
options.trigger收集子节点的值的时机string‘change’
options.validateFirst当某一规则校验不通过时,是否停止剩下的规则的校验booleanfalse
options.validateTrigger校验子节点值的时机string|string[]‘change’
options.valuePropName子节点的值的属性,如 Switch 的是 ‘checked’string‘value’

Form.Item

注意:一个 Form.Item 建议只放一个被 getFieldDecorator 或 v-decorator 装饰过的 child,当有多个被装饰过的 child 时,help required validateStatus 无法自动生成。

参数说明类型默认值版本
colon配合 label 属性使用,表示是否显示 label 后面的冒号booleantrue
extra额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。string|slot
hasFeedback配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用booleanfalse
help提示信息,如不设置,则会根据校验规则自动生成string|slot
htmlFor设置子元素 label htmlFor 属性string1.5.0
labellabel 标签的文本string|slot
labelCollabel 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12}sm: {span: 3, offset: 12}object
labelAlign标签文本对齐方式‘left’ | ‘right’‘right’1.5.0
required是否必填,如不设置,则会根据校验规则自动生成booleanfalse
validateStatus校验状态,如不设置,则会根据校验规则自动生成,可选:’success’ ‘warning’ ‘error’ ‘validating’string
wrapperCol需要为输入控件设置布局样式时,使用该属性,用法同 labelColobject
selfUpdate自定义字段更新逻辑,你可以通过 Form 的 selfUpdate 进行统一设置。当和 Form 同时设置时,以 Item 为准。 说明见下 需 1.3.17 版本以上booleanfalse1.3.17

校验规则

参数说明类型默认值
enum枚举类型string-
len字段长度number-
max最大长度number-
message校验文案string-
min最小长度number-
pattern正则表达式校验RegExp-
required是否必选booleanfalse
transform校验前转换字段值function(value) => transformedValue:any-
type内建校验类型,可选项string‘string’
validator自定义校验(注意,callback 必须被调用function(rule, value, callback)-
whitespace必选时,空格是否会被视为错误booleanfalse

更多高级用法可研究 async-validator

selfUpdate

设置 selfUpdatetrue 后,Form 通过增量方式更新,只更新被修改的字段。大部分场景下,你只需要编写代码即可。而在某些特定场景,例如修改某个字段值后出现新的字段选项、或者纯粹希望表单任意变化都需要进行渲染。你可以通过修改 Form.Item 取消 selfUpdate,或者在 change / onValuesChange 回调中手动调用 this.$forceUpdate() 更新组件。示例

如果你并不精通 Vue,并不建议使用 selfUpdate,如果出现性能问题,可以尝试这把 Form 相关的业务独立到一个单独的组件中,减少组件渲染的消耗。