Form 表单

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

表单

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

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

表单域

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

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

注意:

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

代码演示

Form表单 - 图1

表单联动

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

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

Form表单 - 图2

动态校验规则

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

  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
  30. :label-col="formTailLayout.labelCol"
  31. :wrapper-col="formTailLayout.wrapperCol"
  32. >
  33. <a-checkbox
  34. :checked="checkNick"
  35. @change="handleChange"
  36. >
  37. Nickname is required
  38. </a-checkbox>
  39. </a-form-item>
  40. <a-form-item
  41. :label-col="formTailLayout.labelCol"
  42. :wrapper-col="formTailLayout.wrapperCol"
  43. >
  44. <a-button
  45. type="primary"
  46. @click="check"
  47. >
  48. Check
  49. </a-button>
  50. </a-form-item>
  51. </a-form>
  52. </template>
  53. <script>
  54. const formItemLayout = {
  55. labelCol: { span: 4 },
  56. wrapperCol: { span: 8 },
  57. };
  58. const formTailLayout = {
  59. labelCol: { span: 4 },
  60. wrapperCol: { span: 8, offset: 4 },
  61. };
  62. export default {
  63. data () {
  64. return {
  65. checkNick: false,
  66. formItemLayout,
  67. formTailLayout,
  68. form: this.$form.createForm(this),
  69. };
  70. },
  71. methods: {
  72. check () {
  73. this.form.validateFields(
  74. (err) => {
  75. if (!err) {
  76. console.info('success');
  77. }
  78. },
  79. );
  80. },
  81. handleChange (e) {
  82. this.checkNick = e.target.checked;
  83. this.$nextTick(() => {
  84. this.form.validateFields(['nickname'], { force: true });
  85. });
  86. },
  87. },
  88. };
  89. </script>

Form表单 - 图3

水平登录栏

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

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

Form表单 - 图4

表单布局

表单有三种布局。

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

Form表单 - 图5

自定义校验

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

  • validateStatus: 校验状态,可选 ‘success’, ‘warning’, ‘error’, ‘validating’。
  • hasFeedback:用于给输入框添加反馈图标。
  • help:设置校验文案。
  1. <template>
  2. <a-form>
  3. <a-form-item
  4. :label-col="labelCol"
  5. :wrapper-col="wrapperCol"
  6. label="Fail"
  7. validate-status="error"
  8. help="Should be combination of numbers & alphabets"
  9. >
  10. <a-input
  11. id="error"
  12. placeholder="unavailable choice"
  13. />
  14. </a-form-item>
  15. <a-form-item
  16. :label-col="labelCol"
  17. :wrapper-col="wrapperCol"
  18. label="Warning"
  19. validate-status="warning"
  20. >
  21. <a-input
  22. id="warning"
  23. placeholder="Warning"
  24. />
  25. </a-form-item>
  26. <a-form-item
  27. :label-col="labelCol"
  28. :wrapper-col="wrapperCol"
  29. label="Validating"
  30. has-feedback
  31. validate-status="validating"
  32. help="The information is being validated..."
  33. >
  34. <a-input
  35. id="validating"
  36. placeholder="I'm the content is being validated"
  37. />
  38. </a-form-item>
  39. <a-form-item
  40. :label-col="labelCol"
  41. :wrapper-col="wrapperCol"
  42. label="Success"
  43. has-feedback
  44. validate-status="success"
  45. >
  46. <a-input
  47. id="success"
  48. placeholder="I'm the content"
  49. />
  50. </a-form-item>
  51. <a-form-item
  52. :label-col="labelCol"
  53. :wrapper-col="wrapperCol"
  54. label="Warning"
  55. has-feedback
  56. validate-status="warning"
  57. >
  58. <a-input
  59. id="warning"
  60. placeholder="Warning"
  61. />
  62. </a-form-item>
  63. <a-form-item
  64. :label-col="labelCol"
  65. :wrapper-col="wrapperCol"
  66. label="Fail"
  67. has-feedback
  68. validate-status="error"
  69. help="Should be combination of numbers & alphabets"
  70. >
  71. <a-input
  72. id="error"
  73. placeholder="unavailable choice"
  74. />
  75. </a-form-item>
  76. <a-form-item
  77. :label-col="labelCol"
  78. :wrapper-col="wrapperCol"
  79. label="Success"
  80. has-feedback
  81. validate-status="success"
  82. >
  83. <a-date-picker style="width: 100%" />
  84. </a-form-item>
  85. <a-form-item
  86. :label-col="labelCol"
  87. :wrapper-col="wrapperCol"
  88. label="Warning"
  89. has-feedback
  90. validate-status="warning"
  91. >
  92. <a-time-picker style="width: 100%" />
  93. </a-form-item>
  94. <a-form-item
  95. :label-col="labelCol"
  96. :wrapper-col="wrapperCol"
  97. label="Error"
  98. has-feedback
  99. validate-status="error"
  100. >
  101. <a-select default-value="1">
  102. <a-select-option value="1">
  103. Option 1
  104. </a-select-option>
  105. <a-select-option value="2">
  106. Option 2
  107. </a-select-option>
  108. <a-select-option value="3">
  109. Option 3
  110. </a-select-option>
  111. </a-select>
  112. </a-form-item>
  113. <a-form-item
  114. :label-col="labelCol"
  115. :wrapper-col="wrapperCol"
  116. label="Validating"
  117. has-feedback
  118. validate-status="validating"
  119. help="The information is being validated..."
  120. >
  121. <a-cascader
  122. :default-value="['1']"
  123. :options="[]"
  124. />
  125. </a-form-item>
  126. <a-form-item
  127. label="inline"
  128. :label-col="labelCol"
  129. :wrapper-col="wrapperCol"
  130. style="margin-bottom:0;"
  131. >
  132. <a-form-item
  133. validate-status="error"
  134. help="Please select the correct date"
  135. :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }"
  136. >
  137. <a-date-picker style="width: 100%" />
  138. </a-form-item>
  139. <span :style="{ display: 'inline-block', width: '24px', textAlign: 'center' }">
  140. -
  141. </span>
  142. <a-form-item :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }">
  143. <a-date-picker style="width: 100%" />
  144. </a-form-item>
  145. </a-form-item>
  146. <a-form-item
  147. :label-col="labelCol"
  148. :wrapper-col="wrapperCol"
  149. label="Success"
  150. has-feedback
  151. validate-status="success"
  152. >
  153. <a-input-number style="width: 100%" />
  154. </a-form-item>
  155. </a-form>
  156. </template>
  157. <script>
  158. export default {
  159. data () {
  160. return {
  161. labelCol: {
  162. xs: { span: 24 },
  163. sm: { span: 5 },
  164. },
  165. wrapperCol: {
  166. xs: { span: 24 },
  167. sm: { span: 12 },
  168. },
  169. };
  170. },
  171. };
  172. </script>

Form表单 - 图6

自行处理表单数据

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

Form表单 - 图7

高级搜索

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

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

Form表单 - 图8

自定义表单控件

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

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

Form表单 - 图9

动态增减表单项

动态增加、减少表单项。

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

Form表单 - 图10

弹出层中的新建表单

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

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

Form表单 - 图11

表单数据存储于上层组件

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

Form表单 - 图12

表单数据存储于 Vuex Store 中

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

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

Form表单 - 图13

登录框

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

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

Form表单 - 图14

注册新用户

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

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

Form表单 - 图15

时间类控件

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

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

Form表单 - 图16

校验其他组件

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

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

API

Form

参数说明类型默认值
formForm.create() 包装过的组件会自带 this.form 属性,如果使用template语法,可以使用this.$form.createForm(this, options)object
hideRequiredMark隐藏所有表单项的必选标记Booleanfalse
layout表单布局'horizontal'|'vertical'|'inline''horizontal'

事件

事件名称说明回调参数
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 } })
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 { form: { validateFields } } = this;
  2. validateFields((errors, values) => {
  3. // ...
  4. });
  5. validateFields(['field1', 'field2'], (errors, values) => {
  6. // ...
  7. });
  8. validateFields(['field1', 'field2'], options, (errors, values) => {
  9. // ...
  10. });
参数说明类型默认值
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 接管,这会导致以下结果:

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

特别注意

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
labellabel 标签的文本string|slot
labelCollabel 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12}sm: {span: 3, offset: 12}object
required是否必填,如不设置,则会根据校验规则自动生成booleanfalse
validateStatus校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating'string
wrapperCol需要为输入控件设置布局样式时,使用该属性,用法同 labelColobject

校验规则

参数说明类型默认值
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