• Form 表单
    • 表单
    • 表单域
    • 注意:
    • 代码演示
      • 表单联动
      • 动态校验规则
      • 水平登录栏
      • 表单布局
      • 自定义校验
      • 自行处理表单数据
      • 高级搜索
      • 自定义表单控件
      • 动态增减表单项
      • 弹出层中的新建表单
      • 表单数据存储于上层组件
      • 表单数据存储于 Vuex Store 中
      • 登录框
      • 注册新用户
      • 时间类控件
      • 校验其他组件
  • API
    • Form
    • 事件
    • Form.create(options) | this.$form.createForm(this, options)
      • jsx 使用方式,使用方式和 React 版 antd 一致
      • 单文件 template 使用方式
    • validateFields/validateFieldsAndScroll
      • validateFields 的 callback 参数示例
    • Form.createFormField
    • this.form.getFieldDecorator(id, options) 和 v-decorator="[id, options]"
      • 特别注意
      • getFieldDecorator(id, options) 和 v-decorator="[id, options]" 参数
    • Form.Item
    • 校验规则
    • selfUpdate

    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 :form="form" @submit="handleSubmit">
    3. <a-form-item label="Note" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
    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" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
    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>

    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 :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

    水平登录栏

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

    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 表单 - 图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 default-value="horizontal" @change="handleFormLayoutChange">
    10. <a-radio-button value="horizontal">
    11. Horizontal
    12. </a-radio-button>
    13. <a-radio-button value="vertical">
    14. Vertical
    15. </a-radio-button>
    16. <a-radio-button value="inline">
    17. Inline
    18. </a-radio-button>
    19. </a-radio-group>
    20. </a-form-item>
    21. <a-form-item
    22. label="Field A"
    23. :label-col="formItemLayout.labelCol"
    24. :wrapper-col="formItemLayout.wrapperCol"
    25. >
    26. <a-input placeholder="input placeholder" />
    27. </a-form-item>
    28. <a-form-item
    29. label="Field B"
    30. :label-col="formItemLayout.labelCol"
    31. :wrapper-col="formItemLayout.wrapperCol"
    32. >
    33. <a-input placeholder="input placeholder" />
    34. </a-form-item>
    35. <a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
    36. <a-button type="primary">
    37. Submit
    38. </a-button>
    39. </a-form-item>
    40. </a-form>
    41. </div>
    42. </template>
    43. <script>
    44. export default {
    45. data() {
    46. return {
    47. formLayout: 'horizontal',
    48. };
    49. },
    50. computed: {
    51. formItemLayout() {
    52. const { formLayout } = this;
    53. return formLayout === 'horizontal'
    54. ? {
    55. labelCol: { span: 4 },
    56. wrapperCol: { span: 14 },
    57. }
    58. : {};
    59. },
    60. buttonItemLayout() {
    61. const { formLayout } = this;
    62. return formLayout === 'horizontal'
    63. ? {
    64. wrapperCol: { span: 14, offset: 4 },
    65. }
    66. : {};
    67. },
    68. },
    69. methods: {
    70. handleFormLayoutChange(e) {
    71. this.formLayout = e.target.value;
    72. },
    73. },
    74. };
    75. </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 id="error" placeholder="unavailable choice" />
    11. </a-form-item>
    12. <a-form-item
    13. :label-col="labelCol"
    14. :wrapper-col="wrapperCol"
    15. label="Warning"
    16. validate-status="warning"
    17. >
    18. <a-input id="warning" placeholder="Warning" />
    19. </a-form-item>
    20. <a-form-item
    21. :label-col="labelCol"
    22. :wrapper-col="wrapperCol"
    23. label="Validating"
    24. has-feedback
    25. validate-status="validating"
    26. help="The information is being validated..."
    27. >
    28. <a-input id="validating" placeholder="I'm the content is being validated" />
    29. </a-form-item>
    30. <a-form-item
    31. :label-col="labelCol"
    32. :wrapper-col="wrapperCol"
    33. label="Success"
    34. has-feedback
    35. validate-status="success"
    36. >
    37. <a-input id="success" placeholder="I'm the content" />
    38. </a-form-item>
    39. <a-form-item
    40. :label-col="labelCol"
    41. :wrapper-col="wrapperCol"
    42. label="Warning"
    43. has-feedback
    44. validate-status="warning"
    45. >
    46. <a-input id="warning2" placeholder="Warning" />
    47. </a-form-item>
    48. <a-form-item
    49. :label-col="labelCol"
    50. :wrapper-col="wrapperCol"
    51. label="Fail"
    52. has-feedback
    53. validate-status="error"
    54. help="Should be combination of numbers & alphabets"
    55. >
    56. <a-input id="error2" placeholder="unavailable choice" />
    57. </a-form-item>
    58. <a-form-item
    59. :label-col="labelCol"
    60. :wrapper-col="wrapperCol"
    61. label="Success"
    62. has-feedback
    63. validate-status="success"
    64. >
    65. <a-date-picker style="width: 100%" />
    66. </a-form-item>
    67. <a-form-item
    68. :label-col="labelCol"
    69. :wrapper-col="wrapperCol"
    70. label="Warning"
    71. has-feedback
    72. validate-status="warning"
    73. >
    74. <a-time-picker style="width: 100%" />
    75. </a-form-item>
    76. <a-form-item
    77. :label-col="labelCol"
    78. :wrapper-col="wrapperCol"
    79. label="Error"
    80. has-feedback
    81. validate-status="error"
    82. >
    83. <a-select default-value="1">
    84. <a-select-option value="1">
    85. Option 1
    86. </a-select-option>
    87. <a-select-option value="2">
    88. Option 2
    89. </a-select-option>
    90. <a-select-option value="3">
    91. Option 3
    92. </a-select-option>
    93. </a-select>
    94. </a-form-item>
    95. <a-form-item
    96. :label-col="labelCol"
    97. :wrapper-col="wrapperCol"
    98. label="Validating"
    99. has-feedback
    100. validate-status="validating"
    101. help="The information is being validated..."
    102. >
    103. <a-cascader :default-value="['1']" :options="[]" />
    104. </a-form-item>
    105. <a-form-item
    106. label="inline"
    107. :label-col="labelCol"
    108. :wrapper-col="wrapperCol"
    109. style="margin-bottom:0;"
    110. >
    111. <a-form-item
    112. validate-status="error"
    113. help="Please select the correct date"
    114. :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }"
    115. >
    116. <a-date-picker style="width: 100%" />
    117. </a-form-item>
    118. <span :style="{ display: 'inline-block', width: '24px', textAlign: 'center' }">
    119. -
    120. </span>
    121. <a-form-item :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }">
    122. <a-date-picker style="width: 100%" />
    123. </a-form-item>
    124. </a-form-item>
    125. <a-form-item
    126. :label-col="labelCol"
    127. :wrapper-col="wrapperCol"
    128. label="Success"
    129. has-feedback
    130. validate-status="success"
    131. >
    132. <a-input-number style="width: 100%" />
    133. </a-form-item>
    134. </a-form>
    135. </template>
    136. <script>
    137. export default {
    138. data() {
    139. return {
    140. labelCol: {
    141. xs: { span: 24 },
    142. sm: { span: 5 },
    143. },
    144. wrapperCol: {
    145. xs: { span: 24 },
    146. sm: { span: 12 },
    147. },
    148. };
    149. },
    150. };
    151. </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 :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 表单 - 图7

    高级搜索

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

    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. methods: {
    62. handleSearch(e) {
    63. e.preventDefault();
    64. this.form.validateFields((error, values) => {
    65. console.log('error', error);
    66. console.log('Received values of form: ', values);
    67. });
    68. },
    69. handleReset() {
    70. this.form.resetFields();
    71. },
    72. toggle() {
    73. this.expand = !this.expand;
    74. },
    75. },
    76. };
    77. </script>
    78. <style>
    79. .ant-advanced-search-form {
    80. padding: 24px;
    81. background: #fbfbfb;
    82. border: 1px solid #d9d9d9;
    83. border-radius: 6px;
    84. }
    85. .ant-advanced-search-form .ant-form-item {
    86. display: flex;
    87. }
    88. .ant-advanced-search-form .ant-form-item-control-wrapper {
    89. flex: 1;
    90. }
    91. #components-form-demo-advanced-search .ant-form {
    92. max-width: none;
    93. }
    94. #components-form-demo-advanced-search .search-result-list {
    95. margin-top: 16px;
    96. border: 1px dashed #e9e9e9;
    97. border-radius: 6px;
    98. background-color: #fafafa;
    99. min-height: 200px;
    100. text-align: center;
    101. padding-top: 80px;
    102. }
    103. </style>

    Form 表单 - 图8

    自定义表单控件

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

    • 提供受控属性 value 或其它与 valuePropName-参数) 的值同名的属性。
    • 提供 onChange 事件或 trigger-参数) 的值同名的事件。
    • 不能是函数式组件。
    <template>
      <a-form layout="inline" :form="form" @submit="handleSubmit">
        <a-form-item label="Price">
          <price-input
            v-decorator="[
              'price',
              {
                initialValue: { number: 0, currency: 'rmb' },
                rules: [{ validator: checkPrice }],
              },
            ]"
          />
        </a-form-item>
        <a-form-item>
          <a-button type="primary" html-type="submit">
            Submit
          </a-button>
        </a-form-item>
      </a-form>
    </template>
    
    <script>
    const hasProp = (instance, prop) => {
      const $options = instance.$options || {};
      const propsData = $options.propsData || {};
      return prop in propsData;
    };
    const PriceInput = {
      props: ['value'],
      template: `
        <span>
          <a-input
            type='text'
            :value="number"
            @change="handleNumberChange"
            style="width: 63%; margin-right: 2%;"
          />
          <a-select
            :value="currency"
            style="width: 32%"
            @change="handleCurrencyChange"
          >
            <a-select-option value='rmb'>RMB</a-select-option>
            <a-select-option value='dollar'>Dollar</a-select-option>
          </a-select>
        </span>
      `,
      data() {
        const value = this.value || {};
        return {
          number: value.number || 0,
          currency: value.currency || 'rmb',
        };
      },
      watch: {
        value(val = {}) {
          this.number = val.number || 0;
          this.currency = val.currency || 'rmb';
        },
      },
      methods: {
        handleNumberChange(e) {
          const number = parseInt(e.target.value || 0, 10);
          if (isNaN(number)) {
            return;
          }
          if (!hasProp(this, 'value')) {
            this.number = number;
          }
          this.triggerChange({ number });
        },
        handleCurrencyChange(currency) {
          if (!hasProp(this, 'value')) {
            this.currency = currency;
          }
          this.triggerChange({ currency });
        },
        triggerChange(changedValue) {
          // Should provide an event to pass value to Form.
          this.$emit('change', Object.assign({}, this.$data, changedValue));
        },
      },
    };
    
    export default {
      components: {
        PriceInput,
      },
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'customized_form_controls' });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
            }
          });
        },
        checkPrice(rule, value, callback) {
          if (value.number > 0) {
            callback();
            return;
          }
          callback('Price must greater than zero!');
        },
      },
    };
    </script>
    

    Form 表单 - 图9

    动态增减表单项

    动态增加、减少表单项。

    <template>
      <a-form :form="form" @submit="handleSubmit">
        <a-form-item
          v-for="(k, index) in form.getFieldValue('keys')"
          :key="k"
          v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
          :label="index === 0 ? 'Passengers' : ''"
          :required="false"
        >
          <a-input
            v-decorator="[
              `names[${k}]`,
              {
                validateTrigger: ['change', 'blur'],
                rules: [
                  {
                    required: true,
                    whitespace: true,
                    message: 'Please input passenger\'s name or delete this field.',
                  },
                ],
              },
            ]"
            placeholder="passenger name"
            style="width: 60%; margin-right: 8px"
          />
          <a-icon
            v-if="form.getFieldValue('keys').length > 1"
            class="dynamic-delete-button"
            type="minus-circle-o"
            :disabled="form.getFieldValue('keys').length === 1"
            @click="() => remove(k)"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayoutWithOutLabel">
          <a-button type="dashed" style="width: 60%" @click="add">
            <a-icon type="plus" /> Add field
          </a-button>
        </a-form-item>
        <a-form-item v-bind="formItemLayoutWithOutLabel">
          <a-button type="primary" html-type="submit">
            Submit
          </a-button>
        </a-form-item>
      </a-form>
    </template>
    
    <script>
    let id = 0;
    export default {
      data() {
        return {
          formItemLayout: {
            labelCol: {
              xs: { span: 24 },
              sm: { span: 4 },
            },
            wrapperCol: {
              xs: { span: 24 },
              sm: { span: 20 },
            },
          },
          formItemLayoutWithOutLabel: {
            wrapperCol: {
              xs: { span: 24, offset: 0 },
              sm: { span: 20, offset: 4 },
            },
          },
        };
      },
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'dynamic_form_item' });
        this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
      },
      methods: {
        remove(k) {
          const { form } = this;
          // can use data-binding to get
          const keys = form.getFieldValue('keys');
          // We need at least one passenger
          if (keys.length === 1) {
            return;
          }
    
          // can use data-binding to set
          form.setFieldsValue({
            keys: keys.filter(key => key !== k),
          });
        },
    
        add() {
          const { form } = this;
          // can use data-binding to get
          const keys = form.getFieldValue('keys');
          const nextKeys = keys.concat(id++);
          // can use data-binding to set
          // important! notify form to detect changes
          form.setFieldsValue({
            keys: nextKeys,
          });
        },
    
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
            }
          });
        },
      },
    };
    </script>
    <style>
    .dynamic-delete-button {
      cursor: pointer;
      position: relative;
      top: 4px;
      font-size: 24px;
      color: #999;
      transition: all 0.3s;
    }
    .dynamic-delete-button:hover {
      color: #777;
    }
    .dynamic-delete-button[disabled] {
      cursor: not-allowed;
      opacity: 0.5;
    }
    </style>
    

    Form 表单 - 图10

    弹出层中的新建表单

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

    <template>
      <div>
        <a-button type="primary" @click="showModal">
          New Collection
        </a-button>
        <collection-create-form
          ref="collectionForm"
          :visible="visible"
          @cancel="handleCancel"
          @create="handleCreate"
        />
      </div>
    </template>
    
    <script>
    const CollectionCreateForm = {
      props: ['visible'],
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'form_in_modal' });
      },
      template: `
        <a-modal
          :visible="visible"
          title='Create a new collection'
          okText='Create'
          @cancel="() => { $emit('cancel') }"
          @ok="() => { $emit('create') }"
        >
          <a-form layout='vertical' :form="form">
            <a-form-item label='Title'>
              <a-input
                v-decorator="[
                  'title',
                  {
                    rules: [{ required: true, message: 'Please input the title of collection!' }],
                  }
                ]"
              />
            </a-form-item>
            <a-form-item label='Description'>
              <a-input
                type='textarea'
                v-decorator="['description']"
              />
            </a-form-item>
            <a-form-item class='collection-create-form_last-form-item'>
              <a-radio-group
                v-decorator="[
                  'modifier',
                  {
                    initialValue: 'private',
                  }
                ]"
              >
                  <a-radio value='public'>Public</a-radio>
                  <a-radio value='private'>Private</a-radio>
                </a-radio-group>
            </a-form-item>
          </a-form>
        </a-modal>
      `,
    };
    
    export default {
      components: { CollectionCreateForm },
      data() {
        return {
          visible: false,
        };
      },
      methods: {
        showModal() {
          this.visible = true;
        },
        handleCancel() {
          this.visible = false;
        },
        handleCreate() {
          const form = this.$refs.collectionForm.form;
          form.validateFields((err, values) => {
            if (err) {
              return;
            }
            console.log('Received values of form: ', values);
            form.resetFields();
            this.visible = false;
          });
        },
      },
    };
    </script>
    

    Form 表单 - 图11

    表单数据存储于上层组件

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

    <template>
      <div id="components-form-demo-global-state">
        <customized-form :username="fields.username" @change="handleFormChange" />
        <pre class="language-bash">
          {{ JSON.stringify(fields, null, 2) }}
        </pre>
      </div>
    </template>
    
    <script>
    const CustomizedForm = {
      props: ['username'],
      template: `
        <a-form layout='inline' :form="form">
          <a-form-item label='Username'>
            <a-input
              v-decorator="[
                'username',
                {
                  rules: [{ required: true, message: 'Username is required!' }],
                }
              ]"
            />
          </a-form-item>
        </a-form>
      `,
      created() {
        this.form = this.$form.createForm(this, {
          name: 'global_state',
          onFieldsChange: (_, changedFields) => {
            this.$emit('change', changedFields);
          },
          mapPropsToFields: () => {
            return {
              username: this.$form.createFormField({
                ...this.username,
                value: this.username.value,
              }),
            };
          },
          onValuesChange(_, values) {
            console.log(values);
          },
        });
      },
      watch: {
        username() {
          this.form.updateFields({
            username: this.$form.createFormField({
              ...this.username,
              value: this.username.value,
            }),
          });
        },
      },
    };
    
    export default {
      components: {
        CustomizedForm,
      },
      data() {
        return {
          fields: {
            username: {
              value: 'benjycui',
            },
          },
        };
      },
      methods: {
        handleFormChange(changedFields) {
          console.log('changedFields', changedFields);
          this.fields = { ...this.fields, ...changedFields };
        },
      },
    };
    </script>
    <style>
    #components-form-demo-global-state .language-bash {
      max-width: 400px;
      border-radius: 6px;
      margin-top: 24px;
    }
    </style>
    

    Form 表单 - 图12

    表单数据存储于 Vuex Store 中

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

    <template>
      <div id="components-form-demo-vuex">
        <a-form :form="form" @submit="handleSubmit">
          <a-form-item label="Username">
            <a-input
              v-decorator="[
                'username',
                {
                  rules: [{ required: true, message: 'Username is required!' }],
                },
              ]"
            />
          </a-form-item>
          <a-button type="primary" html-type="submit">
            Submit
          </a-button>
        </a-form>
      </div>
    </template>
    
    <script>
    export default {
      computed: {
        username() {
          return this.$store.state.username;
        },
      },
      watch: {
        username(val) {
          console.log('this.$store.state.username: ', val);
          this.form.setFieldsValue({ username: val });
        },
      },
      created() {
        this.form = this.$form.createForm(this, {
          onFieldsChange: (_, changedFields) => {
            this.$emit('change', changedFields);
          },
          mapPropsToFields: () => {
            return {
              username: this.$form.createFormField({
                value: this.username,
              }),
            };
          },
          onValuesChange: (_, values) => {
            console.log(values);
            // Synchronize to vuex store in real time
            // this.$store.commit('update', values)
          },
        });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
              this.$store.commit('update', values);
            }
          });
        },
      },
    };
    </script>
    <style>
    #components-form-demo-vuex .language-bash {
      max-width: 400px;
      border-radius: 6px;
      margin-top: 24px;
    }
    </style>
    

    Form 表单 - 图13

    登录框

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

    <template>
      <a-form
        id="components-form-demo-normal-login"
        :form="form"
        class="login-form"
        @submit="handleSubmit"
      >
        <a-form-item>
          <a-input
            v-decorator="[
              'userName',
              { rules: [{ required: true, message: 'Please input your username!' }] },
            ]"
            placeholder="Username"
          >
            <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
          </a-input>
        </a-form-item>
        <a-form-item>
          <a-input
            v-decorator="[
              'password',
              { rules: [{ required: true, message: 'Please input your Password!' }] },
            ]"
            type="password"
            placeholder="Password"
          >
            <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
          </a-input>
        </a-form-item>
        <a-form-item>
          <a-checkbox
            v-decorator="[
              'remember',
              {
                valuePropName: 'checked',
                initialValue: true,
              },
            ]"
          >
            Remember me
          </a-checkbox>
          <a class="login-form-forgot" href="">
            Forgot password
          </a>
          <a-button type="primary" html-type="submit" class="login-form-button">
            Log in
          </a-button>
          Or
          <a href="">
            register now!
          </a>
        </a-form-item>
      </a-form>
    </template>
    
    <script>
    export default {
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'normal_login' });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
            }
          });
        },
      },
    };
    </script>
    <style>
    #components-form-demo-normal-login .login-form {
      max-width: 300px;
    }
    #components-form-demo-normal-login .login-form-forgot {
      float: right;
    }
    #components-form-demo-normal-login .login-form-button {
      width: 100%;
    }
    </style>
    

    Form 表单 - 图14

    注册新用户

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

    <template>
      <a-form :form="form" @submit="handleSubmit">
        <a-form-item v-bind="formItemLayout" label="E-mail">
          <a-input
            v-decorator="[
              'email',
              {
                rules: [
                  {
                    type: 'email',
                    message: 'The input is not valid E-mail!',
                  },
                  {
                    required: true,
                    message: 'Please input your E-mail!',
                  },
                ],
              },
            ]"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="Password">
          <a-input
            v-decorator="[
              'password',
              {
                rules: [
                  {
                    required: true,
                    message: 'Please input your password!',
                  },
                  {
                    validator: validateToNextPassword,
                  },
                ],
              },
            ]"
            type="password"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="Confirm Password">
          <a-input
            v-decorator="[
              'confirm',
              {
                rules: [
                  {
                    required: true,
                    message: 'Please confirm your password!',
                  },
                  {
                    validator: compareToFirstPassword,
                  },
                ],
              },
            ]"
            type="password"
            @blur="handleConfirmBlur"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout">
          <span slot="label">
            Nickname&nbsp;
            <a-tooltip title="What do you want others to call you?">
              <a-icon type="question-circle-o" />
            </a-tooltip>
          </span>
          <a-input
            v-decorator="[
              'nickname',
              {
                rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
              },
            ]"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="Habitual Residence">
          <a-cascader
            v-decorator="[
              'residence',
              {
                initialValue: ['zhejiang', 'hangzhou', 'xihu'],
                rules: [
                  { type: 'array', required: true, message: 'Please select your habitual residence!' },
                ],
              },
            ]"
            :options="residences"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="Phone Number">
          <a-input
            v-decorator="[
              'phone',
              {
                rules: [{ required: true, message: 'Please input your phone number!' }],
              },
            ]"
            style="width: 100%"
          >
            <a-select
              slot="addonBefore"
              v-decorator="['prefix', { initialValue: '86' }]"
              style="width: 70px"
            >
              <a-select-option value="86">
                +86
              </a-select-option>
              <a-select-option value="87">
                +87
              </a-select-option>
            </a-select>
          </a-input>
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="Website">
          <a-auto-complete
            v-decorator="['website', { rules: [{ required: true, message: 'Please input website!' }] }]"
            placeholder="website"
            @change="handleWebsiteChange"
          >
            <template slot="dataSource">
              <a-select-option v-for="website in autoCompleteResult" :key="website">
                {{ website }}
              </a-select-option>
            </template>
            <a-input />
          </a-auto-complete>
        </a-form-item>
        <a-form-item
          v-bind="formItemLayout"
          label="Captcha"
          extra="We must make sure that your are a human."
        >
          <a-row :gutter="8">
            <a-col :span="12">
              <a-input
                v-decorator="[
                  'captcha',
                  { rules: [{ required: true, message: 'Please input the captcha you got!' }] },
                ]"
              />
            </a-col>
            <a-col :span="12">
              <a-button>Get captcha</a-button>
            </a-col>
          </a-row>
        </a-form-item>
        <a-form-item v-bind="tailFormItemLayout">
          <a-checkbox v-decorator="['agreement', { valuePropName: 'checked' }]">
            I have read the
            <a href="">
              agreement
            </a>
          </a-checkbox>
        </a-form-item>
        <a-form-item v-bind="tailFormItemLayout">
          <a-button type="primary" html-type="submit">
            Register
          </a-button>
        </a-form-item>
      </a-form>
    </template>
    
    <script>
    const residences = [
      {
        value: 'zhejiang',
        label: 'Zhejiang',
        children: [
          {
            value: 'hangzhou',
            label: 'Hangzhou',
            children: [
              {
                value: 'xihu',
                label: 'West Lake',
              },
            ],
          },
        ],
      },
      {
        value: 'jiangsu',
        label: 'Jiangsu',
        children: [
          {
            value: 'nanjing',
            label: 'Nanjing',
            children: [
              {
                value: 'zhonghuamen',
                label: 'Zhong Hua Men',
              },
            ],
          },
        ],
      },
    ];
    
    export default {
      data() {
        return {
          confirmDirty: false,
          residences,
          autoCompleteResult: [],
          formItemLayout: {
            labelCol: {
              xs: { span: 24 },
              sm: { span: 8 },
            },
            wrapperCol: {
              xs: { span: 24 },
              sm: { span: 16 },
            },
          },
          tailFormItemLayout: {
            wrapperCol: {
              xs: {
                span: 24,
                offset: 0,
              },
              sm: {
                span: 16,
                offset: 8,
              },
            },
          },
        };
      },
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'register' });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFieldsAndScroll((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
            }
          });
        },
        handleConfirmBlur(e) {
          const value = e.target.value;
          this.confirmDirty = this.confirmDirty || !!value;
        },
        compareToFirstPassword(rule, value, callback) {
          const form = this.form;
          if (value && value !== form.getFieldValue('password')) {
            callback('Two passwords that you enter is inconsistent!');
          } else {
            callback();
          }
        },
        validateToNextPassword(rule, value, callback) {
          const form = this.form;
          if (value && this.confirmDirty) {
            form.validateFields(['confirm'], { force: true });
          }
          callback();
        },
        handleWebsiteChange(value) {
          let autoCompleteResult;
          if (!value) {
            autoCompleteResult = [];
          } else {
            autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
          }
          this.autoCompleteResult = autoCompleteResult;
        },
      },
    };
    </script>
    

    Form 表单 - 图15

    时间类控件

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

    <template>
      <a-form :form="form" @submit="handleSubmit">
        <a-form-item v-bind="formItemLayout" label="DatePicker">
          <a-date-picker v-decorator="['date-picker', config]" />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="DatePicker[showTime]">
          <a-date-picker
            v-decorator="['date-time-picker', config]"
            show-time
            format="YYYY-MM-DD HH:mm:ss"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="MonthPicker">
          <a-monthPicker v-decorator="['month-picker', config]" />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="RangePicker">
          <a-range-picker v-decorator="['range-picker', rangeConfig]" />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="RangePicker[showTime]">
          <a-range-picker
            v-decorator="['range-time-picker', rangeConfig]"
            show-time
            format="YYYY-MM-DD HH:mm:ss"
          />
        </a-form-item>
        <a-form-item v-bind="formItemLayout" label="TimePicker">
          <a-time-picker v-decorator="['time-picker', config]" />
        </a-form-item>
        <a-form-item
          :wrapper-col="{
            xs: { span: 24, offset: 0 },
            sm: { span: 16, offset: 8 },
          }"
        >
          <a-button type="primary" html-type="submit">
            Submit
          </a-button>
        </a-form-item>
      </a-form>
    </template>
    <script>
    export default {
      data() {
        return {
          formItemLayout: {
            labelCol: {
              xs: { span: 24 },
              sm: { span: 8 },
            },
            wrapperCol: {
              xs: { span: 24 },
              sm: { span: 16 },
            },
          },
          config: {
            rules: [{ type: 'object', required: true, message: 'Please select time!' }],
          },
          rangeConfig: {
            rules: [{ type: 'array', required: true, message: 'Please select time!' }],
          },
        };
      },
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'time_related_controls' });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, fieldsValue) => {
            if (err) {
              return;
            }
    
            // Should format date value before submit.
            const rangeValue = fieldsValue['range-picker'];
            const rangeTimeValue = fieldsValue['range-time-picker'];
            const values = {
              ...fieldsValue,
              'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
              'date-time-picker': fieldsValue['date-time-picker'].format('YYYY-MM-DD HH:mm:ss'),
              'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
              'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
              'range-time-picker': [
                rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss'),
                rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss'),
              ],
              'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
            };
            console.log('Received values of form: ', values);
          });
        },
      },
    };
    </script>
    

    Form 表单 - 图16

    校验其他组件

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

    <template>
      <a-form id="components-form-demo-validate-other" :form="form" v-bind="formItemLayout" @submit="handleSubmit">
        <a-form-item label="Plain Text">
          <span class="ant-form-text">
            China
          </span>
        </a-form-item>
        <a-form-item label="Select" has-feedback>
          <a-select
            v-decorator="[
              'select',
              { rules: [{ required: true, message: 'Please select your country!' }] },
            ]"
            placeholder="Please select a country"
          >
            <a-select-option value="china">
              China
            </a-select-option>
            <a-select-option value="usa">
              U.S.A
            </a-select-option>
          </a-select>
        </a-form-item>
    
        <a-form-item label="Select[multiple]">
          <a-select
            v-decorator="[
              'select-multiple',
              {
                rules: [
                  { required: true, message: 'Please select your favourite colors!', type: 'array' },
                ],
              },
            ]"
            mode="multiple"
            placeholder="Please select favourite colors"
          >
            <a-select-option value="red">
              Red
            </a-select-option>
            <a-select-option value="green">
              Green
            </a-select-option>
            <a-select-option value="blue">
              Blue
            </a-select-option>
          </a-select>
        </a-form-item>
    
        <a-form-item label="InputNumber">
          <a-input-number v-decorator="['input-number', { initialValue: 3 }]" :min="1" :max="10" />
          <span class="ant-form-text">
            machines
          </span>
        </a-form-item>
    
        <a-form-item label="Switch">
          <a-switch v-decorator="['switch', { valuePropName: 'checked' }]" />
        </a-form-item>
    
        <a-form-item label="Slider">
          <a-slider
            v-decorator="['slider']"
            :marks="{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }"
          />
        </a-form-item>
    
        <a-form-item label="Radio.Group">
          <a-radio-group v-decorator="['radio-group']">
            <a-radio value="a">
              item 1
            </a-radio>
            <a-radio value="b">
              item 2
            </a-radio>
            <a-radio value="c">
              item 3
            </a-radio>
          </a-radio-group>
        </a-form-item>
    
        <a-form-item label="Radio.Button">
          <a-radio-group v-decorator="['radio-button']">
            <a-radio-button value="a">
              item 1
            </a-radio-button>
            <a-radio-button value="b">
              item 2
            </a-radio-button>
            <a-radio-button value="c">
              item 3
            </a-radio-button>
          </a-radio-group>
        </a-form-item>
    
        <a-form-item label="Checkbox.Group">
          <a-checkbox-group
            v-decorator="['checkbox-group', { initialValue: ['A', 'B'] }]"
            style="width: 100%;"
          >
            <a-row>
              <a-col :span="8">
                <a-checkbox value="A">
                  A
                </a-checkbox>
              </a-col>
              <a-col :span="8">
                <a-checkbox disabled value="B">
                  B
                </a-checkbox>
              </a-col>
              <a-col :span="8">
                <a-checkbox value="C">
                  C
                </a-checkbox>
              </a-col>
              <a-col :span="8">
                <a-checkbox value="D">
                  D
                </a-checkbox>
              </a-col>
              <a-col :span="8">
                <a-checkbox value="E">
                  E
                </a-checkbox>
              </a-col>
            </a-row>
          </a-checkbox-group>
        </a-form-item>
    
        <a-form-item label="Rate">
          <a-rate v-decorator="['rate', { initialValue: 3.5 }]" allow-half />
        </a-form-item>
    
        <a-form-item label="Upload" extra="longgggggggggggggggggggggggggggggggggg">
          <a-upload
            v-decorator="[
              'upload',
              {
                valuePropName: 'fileList',
                getValueFromEvent: normFile,
              },
            ]"
            name="logo"
            action="/upload.do"
            list-type="picture"
          >
            <a-button> <a-icon type="upload" /> Click to upload </a-button>
          </a-upload>
        </a-form-item>
    
        <a-form-item label="Dragger">
          <div class="dropbox">
            <a-upload-dragger
              v-decorator="[
                'dragger',
                {
                  valuePropName: 'fileList',
                  getValueFromEvent: normFile,
                },
              ]"
              name="files"
              action="/upload.do"
            >
              <p class="ant-upload-drag-icon">
                <a-icon type="inbox" />
              </p>
              <p class="ant-upload-text">
                Click or drag file to this area to upload
              </p>
              <p class="ant-upload-hint">
                Support for a single or bulk upload.
              </p>
            </a-upload-dragger>
          </div>
        </a-form-item>
    
        <a-form-item :wrapper-col="{ span: 12, offset: 6 }">
          <a-button type="primary" html-type="submit">
            Submit
          </a-button>
        </a-form-item>
      </a-form>
    </template>
    
    <script>
    export default {
      data: () => ({
        formItemLayout: {
          labelCol: { span: 6 },
          wrapperCol: { span: 14 },
        },
      }),
      beforeCreate() {
        this.form = this.$form.createForm(this, { name: 'validate_other' });
      },
      methods: {
        handleSubmit(e) {
          e.preventDefault();
          this.form.validateFields((err, values) => {
            if (!err) {
              console.log('Received values of form: ', values);
            }
          });
        },
        normFile(e) {
          console.log('Upload event:', e);
          if (Array.isArray(e)) {
            return e;
          }
          return e && e.fileList;
        },
      },
    };
    </script>
    <style>
    #components-form-demo-validate-other .dropbox {
      height: 180px;
      line-height: 1.5;
    }
    </style>
    

    API

    Form

    参数说明类型默认值
    formForm.create() 包装过的组件会自带 this.form 属性,如果使用 template 语法,可以使用 this.$form.createForm(this, options)object
    hideRequiredMark隐藏所有表单项的必选标记Booleanfalse
    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 版本以上booleanfalse

    事件

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

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

    使用方式如下:

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

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

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

    单文件 template 使用方式

    <template>
      <a-form :form="form" />
    </template>
    <script>
      export default {
        beforeCreate() {
          this.form = this.$form.createForm(this, options);
        },
      };
    </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

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

    validateFields 的 callback 参数示例

    • errors:
    {
      "userName": {
        "errors": [
          {
            "message": "Please input your username!",
            "field": "userName"
          }
        ]
      },
      "password": {
        "errors": [
          {
            "message": "Please input your Password!",
            "field": "password"
          }
        ]
      }
    }
    
    • values:
    {
      "userName": "username",
      "password": "password",
    }
    

    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 来动态改变表单值。

    特别注意

    • getFieldDecoratorv-decorator 不能用于装饰纯函数组件。
    • 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
    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
    selfUpdate自定义字段更新逻辑,你可以通过 Form 的 selfUpdate 进行统一设置。当和 Form 同时设置时,以 Item 为准。 说明见下 需 1.3.17 版本以上booleanfalse

    校验规则

    参数说明类型默认值
    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 相关的业务独立到一个单独的组件中,减少组件渲染的消耗。