Form

Form consists of input, radio, select, checkbox and so on. With form, you can collect, verify and submit data

Tip

Applicable to any component that can bind value through v-model. example:

  • Support el-input el-switch input
  • Not support el-upload … (The relevant components need to be rewritten to support v-model components)

Use

Basic Use

When columns is bound to a reactive array, changes in the array will affect form changes (dynamic form). If you don’t need a dynamic form, it is recommended to bind an ordinary array.

Name
Address
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = [
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ]
    const submit = (done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    }

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Intellisense

Use the defineFormColumns defineFormMenuColumns defineFormSubmit defineComponentProps to make it easier to define columns

Name
Address
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :menu="menu"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
  defineFormColumns,
  defineFormMenuColumns,
  defineFormSubmit,
} from 'element-pro-components'

export default defineComponent({
  setup() {
    const menu = defineFormMenuColumns({
      submitText: 'Create',
      reset: false,
    })
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      menu,
      form,
      columns,
      submit,
    }
  },
})
</script>

Nested value

Support for set the objects or arrays with nested structures value, only need to configure the prop

{}

Break
Object
Array

Prevent assignment to nested keys when prop has a default value

<template>
  <p>{{ form }}</p>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({ 'a.b': undefined })
    const columns = defineFormColumns([
      {
        label: 'Break',
        prop: 'a.b',
        component: 'el-input',
      },
      {
        label: 'Object',
        prop: 'a.b.c',
        component: 'el-input',
      },
      {
        label: 'Array',
        prop: 'b[0]',
        component: 'el-input',
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Custom Component

Set component in columns attribute to dfine what component the item generates, that component should can bind value through v-model. props can be passed to the component through props, render-function can be passed to the component thrrough slots in props

input
Search
input-tag
radio
checkbox
select
Select
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, markRaw, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const list = ref([
      { value: 'Go', label: 'go' },
      { value: 'JavaScript', label: 'javascript' },
      { value: 'Python', label: 'python' },
      { value: 'Dart', label: 'dart' },
      { value: 'V', label: 'v' },
    ])
    const columns = defineFormColumns([
      {
        label: 'input',
        prop: 'input',
        component: 'el-input',
        props: {
          clearable: true,
          placeholder: 'Please input',
          prefixIcon: markRaw(Search),
          slots: {
            append: () => 'Search',
          },
        },
      },
      {
        label: 'input-tag',
        prop: 'inputTag',
        component: 'pro-input-tag',
        props: {
          placeholder: 'Please click the space button after input',
        },
      },
      {
        label: 'radio',
        prop: 'radio',
        component: 'pro-radio',
        props: {
          data: list.value,
        },
      },
      {
        label: 'checkbox',
        prop: 'checkbox',
        component: 'pro-checkbox',
        props: {
          data: list.value,
        },
      },
      {
        label: 'select',
        prop: 'select',
        component: 'pro-select',
        props: {
          data: list.value,
        },
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Custom local registration component

Local component can be passed directly through component in columns attribute (marking the component with markRaw)

radio
switch
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :gutter="20"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref, markRaw } from 'vue'
import { ElSwitch, ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const list = ref([
      { value: 'Go', label: 'go' },
      { value: 'JavaScript', label: 'javascript' },
      { value: 'Python', label: 'python' },
      { value: 'Dart', label: 'dart' },
      { value: 'V', label: 'v' },
    ])
    const form = ref({})
    const columns = ref(
      defineFormColumns([
        {
          label: 'radio',
          prop: 'radio',
          component: 'pro-radio',
          props: {
            data: list,
          },
        },
        {
          label: 'switch',
          prop: 'switch',
          component: markRaw(ElSwitch),
        },
      ])
    )
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Configure the v-model arguments for component

By default, the ProForm component only supports components that bind values through v-model. If you need to use other arguments to bind values, you can configure it through modelKey.

Use outside the ProForm
Use in the ProForm

In addition to supporting strings, modelKey also supports passing in [prop, event] (prop is used to configure the parameters of the bound value, event is used to configure the event of the bound value)

<template>
  <div>
    <span class="el-form-item__label">Use outside the ProForm</span>
    <my-input v-model:value="form.value" />
  </div>
  <pro-form
    v-model="form"
    :columns="columns"
    inline
    @submit="submit"
  />
</template>

<script>
import { defineComponent, h, markRaw, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

const MyInput = {
  name: 'MyInput',
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  emits: ['update:value'],
  setup(props, { emit }) {
    return () =>
      h('input', {
        value: props.value,
        onInput: (e) => emit('update:value', e.target.value),
      })
  },
}

export default defineComponent({
  components: { MyInput },
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Use in the ProForm',
        prop: 'value',
        component: markRaw(MyInput),
        modelKey: 'value', // or ['value', 'onUpdate:value']
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Slots

Tip

Starting from 1.2.0, the [prop] related slots need to be prefixed with form- to use

Directly add some slot with form-[prop] in the template

picture
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  >
    <template #form-slot-label>
      <picture-rounded class="icon-picture" />
      <span>picture</span>
    </template>
    <template #form-slot="{ value, setValue }">
      <el-upload
        :show-file-list="false"
        :before-upload="(file) => beforeUpload(file, setValue)"
        action=""
        class="avatar-uploader"
      >
        <img
          v-if="value"
          :src="value"
          class="avatar"
        >
        <plus
          v-else
          class="icon-uploader"
        />
      </el-upload>
    </template>
  </pro-form>
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, PictureRounded } from '@element-plus/icons-vue'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  components: { Plus, PictureRounded },
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        prop: 'slot',
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    function beforeUpload(file, setValue) {
      // Simulate uploading pictures
      const fileReader = new FileReader()
      fileReader.onloadend = (e) => setValue(e.target.result)
      fileReader.readAsDataURL(file)
      return false
    }

    return {
      form,
      columns,
      submit,
      beforeUpload,
    }
  },
})
</script>

<style>
.icon-picture {
  margin-right: 6px;
  width: 16px;
  height: 16px;
}
.avatar-uploader .el-upload {
  width: 178px;
  height: 178px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  overflow: hidden;
  text-align: center;
  line-height: 200px;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader .icon-uploader {
  width: 50px;
  height: 50px;
  color: #8c939d;
}
.avatar-uploader .avatar {
  display: block;
  width: 100%;
  height: 100%;
}
</style>

Custom Menu

Set menu attribute to enable custom menu

Menu can also be configured through Localization

Name
Address
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :menu="menu"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
  defineFormColumns,
  defineFormMenuColumns,
  defineFormSubmit,
} from 'element-pro-components'

export default defineComponent({
  setup() {
    const menu = defineFormMenuColumns({
      submitText: 'Create',
      reset: false,
    })
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      menu,
      form,
      columns,
      submit,
    }
  },
})
</script>

Custom sub-forms

Set children in columns attribute to enable custom sub-forms

Goods
Spec

Use max to limit the maximum number of sub-forms

<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Goods',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Spec',
        prop: 'spec',
        size: 'small',
        max: 3,
        children: [
          {
            label: 'Weight',
            prop: 'weight',
            component: 'el-input',
          },
          {
            label: 'Size',
            prop: 'size',
            max: 1,
            children: [
              {
                label: 'Length',
                prop: 'length',
                component: 'el-input',
              },
              {
                label: 'Width',
                prop: 'width',
                component: 'el-input',
              },
              {
                label: 'Height',
                prop: 'height',
                component: 'el-input',
              },
            ],
          },
        ],
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Validation

Set rules attribute to enable validation, or Set rules in columns attribute to enable validation, or reference ${parental prop}.${current index}.${current prop}

Date
User
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :rules="rules"
    label-width="100px"
    @submit="submit"
    @reset="resetForm"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const rules = {
      date: { required: true, message: 'Please input date', trigger: 'blur' },
      user: { required: true, message: 'Please input user', trigger: 'blur' },
    }
    const columns = defineFormColumns([
      {
        label: 'Date',
        prop: 'date',
        component: 'el-input',
      },
      {
        label: 'User',
        prop: 'user',
        max: 3,
        size: 'small',
        children: [
          {
            label: 'Name',
            prop: 'name',
            component: 'el-input',
            rules: {
              required: true,
              message: 'Please input Name',
              trigger: 'blur',
            },
          },
          {
            label: 'Address',
            prop: 'address',
            component: 'el-input',
          },
        ],
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    function resetForm() {
      ElMessage('reset form')
      console.log('reset form')
    }

    return {
      form,
      rules,
      columns,
      submit,
      resetForm,
    }
  },
})
</script>

Dynamically Form

If the columns with reactive, the dynamically modified columns form will also change accordingly

Label-0
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :menu="menu"
    label-width="100px"
    @submit="submit"
  >
    <template #menu-left>
      <el-button
        v-show="columns.length < 5"
        @click="add"
      >
        Add One
      </el-button>
    </template>
    <template #menu-right>
      <el-button
        v-show="columns.length"
        @click="del"
      >
        Delete One
      </el-button>
    </template>
  </pro-form>
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
  defineFormColumns,
  defineFormMenuColumns,
  defineFormSubmit,
} from 'element-pro-components'

export default defineComponent({
  setup() {
    const menu = defineFormMenuColumns({
      submit: false,
      reset: false,
    })
    const count = ref(0)
    const form = ref({})
    const columns = ref(
      defineFormColumns([
        {
          label: 'Label-0',
          prop: 'prop0',
          component: 'el-input',
        },
      ])
    )
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    function add() {
      count.value++
      columns.value.push({
        label: 'Label-' + count.value,
        prop: 'prop' + count.value,
        component: 'el-input',
      })
    }

    function del() {
      const index = Math.floor(Math.random() * columns.value.length)
      columns.value.splice(index, 1)
    }

    return {
      menu,
      form,
      columns,
      submit,
      add,
      del,
    }
  },
})
</script>

Linkage Form

By controlling the show field of columns, the display of the form can be dynamically controlled, making it easy to achieve linked forms

Plan
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { computed, defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = computed(() =>
      defineFormColumns([
        {
          label: 'Plan',
          prop: 'plan',
          component: 'pro-radio',
          rules: {
            required: true,
            message: 'Please select a plan',
            trigger: 'blue',
          },
          props: {
            data: [
              { label: 'Basic', value: 'basic' },
              { label: 'Pro', value: 'pro' },
            ],
          },
        },
        {
          label: 'Name',
          prop: 'name',
          component: 'el-input',
          show: !!form.value.plan,
        },
        getContentColumn(form.value.plan === 'basic'),
        {
          label: 'List',
          prop: 'list',
          max: 5,
          show: form.value.plan === 'pro',
          children: [
            {
              label: 'Date',
              prop: 'date',
              component: 'el-date-picker',
            },
            getContentColumn(form.value.plan === 'pro'),
          ],
        },
      ])
    )
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    function getContentColumn(show) {
      return {
        label: 'Content',
        prop: 'content',
        component: 'el-input',
        show,
        props: {
          type: 'textarea',
          rows: 2,
        },
      }
    }

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Layout

Use the same way as el-row el-col (el-row corresponds to pro-form; el-col corresponds to columns) Invalid when inline is true

Goods
Weight
Count
Length
Width
Height
Price
MarketPrice
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :gutter="20"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Goods',
        prop: 'name',
        component: 'el-input',
        span: 24,
      },
      {
        label: 'Weight',
        prop: 'weight',
        component: 'el-input',
        xs: 24,
        md: 12,
      },
      {
        label: 'Count',
        prop: 'count',
        component: 'el-input',
        xs: 24,
        md: 12,
      },
      {
        label: 'Length',
        prop: 'length',
        component: 'el-input',
        xs: 24,
        md: 8,
      },
      {
        label: 'Width',
        prop: 'width',
        component: 'el-input',
        xs: 24,
        md: 8,
      },
      {
        label: 'Height',
        prop: 'height',
        component: 'el-input',
        xs: 24,
        md: 8,
      },
      {
        label: 'Price',
        prop: 'price',
        component: 'el-input',
        xs: 24,
        md: 12,
      },
      {
        label: 'MarketPrice',
        prop: 'marketPrice',
        component: 'el-input',
        xs: 24,
        md: 12,
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Inline Form

Set the inline attribute to true and the form will be inline

Name
Address
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :menu="menu"
    inline
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
  defineFormColumns,
  defineFormMenuColumns,
  defineFormSubmit,
} from 'element-pro-components'

export default defineComponent({
  setup() {
    const menu = defineFormMenuColumns({
      submitText: 'Search',
      reset: false,
    })
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      menu,
      form,
      columns,
      submit,
    }
  },
})
</script>

Custom Alignment

The label-position attribute decides how labels align

Name
Address

When the breakpoint is xs, the defaults value is top, otherwise is right

<template>
  <pro-radio-button
    v-model="labelPosition"
    :data="data"
    style="margin-bottom: 18px"
  />
  <pro-form
    v-model="form"
    :columns="columns"
    :label-position="labelPosition"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const labelPosition = ref('left')
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ])
    const data = [
      { label: 'Left', value: 'left' },
      { label: 'Right', value: 'right' },
      { label: 'Top', value: 'top' },
    ]
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      labelPosition,
      data,
      form,
      columns,
      submit,
    }
  },
})
</script>

Array Form

The array form is the same as the sub-form. It is used to handle the situation where an array needs to be entered. Just configure the array to enable

Control the maximum number of forms through max

<template>
  <pro-form
    v-model="form"
    :columns="columns"
    :max="3"
    array
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'

export default defineComponent({
  setup() {
    const form = ref()
    const columns = [
      {
        label: 'Name',
        prop: 'name',
        component: 'el-input',
      },
      {
        label: 'Address',
        prop: 'address',
        component: 'el-input',
      },
    ]
    const submit = (done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    }

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Group Form

The Group Form used to display ordinary forms in groups

Name
First Name
Last Name
Address
City
Address
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        type: 'group',
        children: [
          {
            label: 'First Name',
            prop: 'firstName',
            component: 'el-input',
            md: 12,
          },
          {
            label: 'Last Name',
            prop: 'lastName',
            component: 'el-input',
            md: 12,
          },
        ],
      },
      {
        label: 'Address',
        type: 'group',
        children: [
          {
            label: 'City',
            prop: 'city',
            component: 'el-input',
            md: 12,
          },
          {
            label: 'Address',
            prop: 'address',
            component: 'el-input',
            md: 12,
          },
        ],
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Collapse Form

Collapsible group form

<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        type: 'collapse',
        children: [
          {
            label: 'First Name',
            prop: 'firstName',
            component: 'el-input',
            md: 12,
          },
          {
            label: 'Last Name',
            prop: 'lastName',
            component: 'el-input',
            md: 12,
          },
        ],
      },
      {
        label: 'Address',
        type: 'collapse',
        children: [
          {
            label: 'City',
            prop: 'city',
            component: 'el-input',
            md: 12,
          },
          {
            label: 'Address',
            prop: 'address',
            component: 'el-input',
            md: 12,
          },
        ],
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Tabs Form

Switch different forms through tabs

Name
Password
<template>
  <pro-form
    ref="formRef"
    v-model="form"
    :columns="columns"
    :rules="rules"
    label-position="top"
    class="docs-tabs-form"
    @submit="submit"
    @tab-change="tabChange"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const loginRules = {
      username: {
        required: true,
        message: 'Please input name',
        trigger: 'blur',
      },
      password: {
        required: true,
        message: 'Please input password',
        trigger: 'blur',
      },
    }
    const registerRules = {
      name: { required: true, message: 'Please input name', trigger: 'blur' },
      email: { required: true, message: 'Please input email', trigger: 'blur' },
      password: {
        required: true,
        message: 'Please input password',
        trigger: 'blur',
      },
    }

    const form = ref({})
    const formRef = ref()
    const rules = ref(loginRules)

    const columns = defineFormColumns([
      {
        label: 'Sign in',
        prop: 'login',
        type: 'tabs',
        children: [
          {
            label: 'Name',
            prop: 'username',
            component: 'el-input',
            props: {
              clearable: true,
              placeholder: 'Please input name',
            },
          },
          {
            label: 'Password',
            prop: 'password',
            component: 'el-input',
            props: {
              type: 'password',
              clearable: true,
              showPassword: true,
              placeholder: 'Please input password',
            },
          },
        ],
      },
      {
        label: 'Sign up',
        prop: 'register',
        type: 'tabs',
        children: [
          {
            label: 'Name',
            prop: 'name',
            component: 'el-input',
            props: {
              clearable: true,
              placeholder: 'Please input name',
            },
          },
          {
            label: 'Email',
            prop: 'email',
            component: 'el-input',
            props: {
              clearable: true,
              placeholder: 'Please input email',
            },
          },
          {
            label: 'Password',
            prop: 'password',
            component: 'el-input',
            props: {
              clearable: true,
              placeholder: 'Please input password',
            },
          },
        ],
      },
    ])

    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    const tabChange = (prop) => {
      rules.value = prop === 'login' ? loginRules : registerRules
      setTimeout(() => {
        formRef.value.resetFields()
      })
    }

    return {
      form,
      formRef,
      columns,
      rules,
      submit,
      tabChange,
    }
  },
})
</script>

<style scoped>
.docs-tabs-form {
  margin: 0 auto;
  width: 50%;
  min-width: 300px;
  max-width: 500px;
}
.docs-tabs-form :deep(.pro-tabs-form .el-tabs__header) {
  text-align: center;
}
.docs-tabs-form :deep(.pro-tabs-form .el-tabs__header .el-tabs__nav-wrap) {
  --el-font-size-base: 20px;
  display: inline-block;
}
</style>

Steps Form

Split a form into several steps for input

0
Name
0
Address
First Name
Last Name
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const form = ref({})
    const columns = defineFormColumns([
      {
        label: 'Name',
        type: 'steps',
        children: [
          {
            label: 'First Name',
            prop: 'firstName',
            component: 'el-input',
            rules: { required: true, message: 'Please input', trigger: 'blur' },
          },
          {
            label: 'Last Name',
            prop: 'lastName',
            component: 'el-input',
            rules: { required: true, message: 'Please input', trigger: 'blur' },
          },
        ],
      },
      {
        label: 'Address',
        type: 'steps',
        children: [
          {
            label: 'City',
            prop: 'city',
            component: 'el-input',
            rules: { required: true, message: 'Please input', trigger: 'blur' },
          },
          {
            label: 'Address',
            prop: 'address',
            component: 'el-input',
            rules: { required: true, message: 'Please input', trigger: 'blur' },
          },
        ],
      },
    ])
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })

    return {
      form,
      columns,
      submit,
    }
  },
})
</script>

Async Form

To implement Async Form, columns must be bound to a reactive array

<template>
  <div style="margin-bottom: 20px">
    <el-button
      type="primary"
      @click="createForm"
    >
      Load Form
    </el-button>
    <el-button
      type="info"
      @click="createDict"
    >
      Load Dict
    </el-button>
    <el-button
      type="danger"
      @click="destroyForm"
    >
      Destroy
    </el-button>
  </div>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script>
import { defineComponent, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { defineFormColumns, defineFormSubmit } from 'element-pro-components'

export default defineComponent({
  setup() {
    const list = ref([])
    const form = ref({})
    const columns = ref(defineFormColumns([]))
    const submit = defineFormSubmit((done, isValid, invalidFields) => {
      ElMessage(`submit: ${isValid}`)
      console.log(form.value, isValid, invalidFields)
      setTimeout(() => {
        done()
      }, 1000)
    })
    const createForm = () => {
      columns.value = defineFormColumns([
        {
          label: 'Name',
          prop: 'name',
          component: 'el-input',
        },
        {
          label: 'Language',
          prop: 'language',
          component: 'pro-radio',
          props: {
            data: list,
          },
        },
      ])
    }
    const createDict = () => {
      list.value = [
        { value: 'Go', label: 'go' },
        { value: 'JavaScript', label: 'javascript' },
        { value: 'Python', label: 'python' },
        { value: 'Dart', label: 'dart' },
        { value: 'V', label: 'v' },
      ]
    }
    const destroyForm = () => {
      columns.value = []
      list.value = []
    }

    return {
      form,
      columns,
      submit,
      createForm,
      createDict,
      destroyForm,
    }
  },
})
</script>

TypeScript

The function defineFormColumns supports passing in a Generics type to infer the value of prop, The function defineComponentProps supports passing in a Generics type to help input the props value

Name
Search
Status
<template>
  <pro-form
    v-model="form"
    :columns="columns"
    label-width="100px"
    @submit="submit"
  />
</template>

<script setup lang="ts">
import { markRaw, ref } from 'vue'
import { ElMessage, ElSwitch } from 'element-plus'
import {
  defineFormColumns,
  defineFormSubmit,
  defineComponentProps,
} from 'element-pro-components'

interface Form {
  name?: string
  status?: boolean
}

const form = ref<Form>({})
const columns = defineFormColumns<Form>([
  {
    label: 'Name',
    prop: 'name',
    component: 'ElInput',
    props: defineComponentProps<'ElInput'>({
      clearable: true,
      placeholder: 'Please input your name',
      slots: {
        append: () => 'Search',
      },
    }),
  },
  {
    label: 'Status',
    prop: 'status',
    component: markRaw(ElSwitch),
    props: defineComponentProps<typeof ElSwitch>({
      inlinePrompt: true,
      activeText: 'Y',
      inactiveText: 'N',
    }),
  },
])
const submit = defineFormSubmit((done, isValid, invalidFields) => {
  ElMessage(`submit: ${isValid}`)
  console.log(form.value, isValid, invalidFields)
  setTimeout(() => {
    done()
  }, 1000)
})
</script>

Props

NameDescriptionTypeOptionsDefault
v-modelbinding valueobject / array--
columnsto generate form components, reference columnsarray--
menuconfig the menu content, reference menuobject--
rulesvalidation rules of formobject--
inlinewhether the form is inlineboolean-false
arraywhether the form is ArrayFormboolean-
maxlimit the maximum number of ArrayForm componentnumber--
label-positionposition of label. If set to ‘left’ or ‘right’, label-width prop is also requiredstringright / left / topright
label-widthwidth of label, e.g. ‘50px’. All its direct child form items will inherit this value. Width auto is supported.string--
label-suffixsuffix of the labelstring--
hide-required-asteriskwhether required fields should have a red asterisk (star) beside their labelsboolean-false
show-messagewhether to show the error messageboolean-true
inline-messagewhether to display the error message inline with the form itemboolean-false
status-iconwhether to display an icon indicating the validation resultboolean-false
validate-on-rule-changewhether to trigger validation when the rules prop is changedboolean-true
sizecontrol the size of components in this formstringlarge / default /small-
disabledwhether to disabled all components in this form. If set to true, it cannot be overridden by its inner components’ disabled propboolean-false
scroll-to-errorWhen validation fails, scroll to the first error form entryboolean-false
guttergrid spacingnumber-0
justifyhorizontal alignment of flex layoutstringstart / end / center / space-around / space-between / spacing-evenlystart
alignvertical alignment of flex layoutstringtop / middle / bottomtop

columns

NameDescriptionTypeOptionsDefault
propa key of v-model (unique value)string--
labellabel textstring--
componentbinding componentstring--
propstransfer props to the current componentobject--
modelKeythe arguments name bound to the v-model of the current componentstring / [string, string]--
childrengroup form or sub-form contentarray--
typetype of children internal formsstringarray / group / tabs / collapse / stepsarray
maxlimit the maximum number of type=arraynumber--
showwhether to show the current componentboolean-true
labelWidthwidth of label, e.g. ‘50px’. Width auto is supported.string--
requiredwhether the field is required or not, will be determined by validation rules if omittedboolean-false
rulesvalidation rules of formobject--
errorfield error message, set its value and the field will validate error and show this message immediatelystring--
showMessagewhether to show the error messageboolean-true
inlineMessageinline style validate messageboolean-false
sizecontrol the size of components in this form-itemstringlarge / default /small-
spannumber of column the grid spansnumber-24
offsetnumber of spacing on the left side of the gridnumber-0
pushnumber of columns that grid moves to the rightnumber-0
pullnumber of columns that grid moves to the leftnumber-0
xs<768px Responsive columns or column props objectnumber / object (e.g. {span: 4, offset: 4})--
sm≥768px Responsive columns or column props objectnumber / object (e.g. {span: 4, offset: 4})--
md≥992px Responsive columns or column props objectnumber / object (e.g. {span: 4, offset: 4})--
lg≥1200px Responsive columns or column props objectnumber / object (e.g. {span: 4, offset: 4})--
xl≥1920px Responsive columns or column props objectnumber / object (e.g. {span: 4, offset: 4})--
disabledwhether Tab is disabled, work on type=tabs or type=collapsebooleanfalse
closablewhether Tab is closable, work on type=tabsbooleanfalse
lazywhether Tab is lazily rendered, work on type=tabsbooleanfalse
descriptionstep description, work on type=stepsstring
iconstep custom icon, work on type=stepsstring / Component
statuscurrent status, work on type=stepsstringwait / process / finish / error / success

about props

The props attribute will all be passed to the component. For events need to be bound by on[Event]. example: change -> onChange, input -> onInput

props: {
  clearable: true,
  'prefix-icon': 'el-icon-search',
  suffixIcon: 'el-icon-date',
  onChange: e => console.log(e),
}
NameDescriptionTypeDefault
submitwhether to display a submit buttonbooleantrue
submitTextthe text of submit buttonstringSubmit
submitPropsthe props of submit button, reference el-buttonobject{ type: ‘primary’ }
resetWhether to display a reset buttonbooleantrue
resetTextthe text of reset buttonstringReset
resetPropsthe props of reset button, reference el-buttonobject-
prevTextthe text of prev buttonstringPrev
prevPropsthe props of prev button, reference el-buttonobject-
nextTextthe text of next buttonstringNext
nextPropsthe props of next button, reference el-buttonobject-

Events

NameDescriptionParameters
submittriggers when the submit clickdone, isValid, invalidFields
resettriggers when the reset click-
validatetriggers after a form item is validatedprop, isValid, invalidFields
add-itemtriggers when the add clickindexes: number[]
remove-itemtriggers when the remove clickindexes: number[]
collapse-changetriggers when the collapse changeactive: CollapseModelValue
tab-changetriggers when the tab changename: TabPaneName
step-changetriggers when the step changeactive: string | number

Methods

NameDescriptionName
validatevalidate the whole form. Takes a callback as a param. After validation, the callback will be executed with two params: a boolean indicating if the validation has passed, and an object containing all fields that fail the validation. Returns a promise if callback is omittedFunction(callback: Function(boolean, object))
validateFieldvalidate one or several form itemsFunction(props: string | array, callback: Function(errorMessage: string))
resetFieldsreset all the fields and remove validation result-
scrollToFieldScroll to the specified form fieldFunction(prop: string)
clearValidateclear validation message for certain fields. The parameter is prop name or an array of prop names of the form items whose validation messages will be removed. When omitted, all fields’ validation messages will be clearedFunction(props: string | array)

Slots

NameDescriptionType
-anything inserted before the menu-
menu-leftcontrol the menu left display content{ loading: boolean }
menu-rightcontrol the menu right display content{ loading: boolean }
form-[prop]control the Item display content{ item: object, indexes?: number[], value: any, setValue: (value: any) => void }
form-[prop]-labelcontrol the Item label display content{ item: object, indexes?: number[] }
form-[prop]-errorcontrol the Item error display content{ error, item: object, indexes?: number[] }

Tip

[prop] the prop of columns