• 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
    • 校验规则

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

    特别注意

    • 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

    校验规则

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