Skip to content

Form

Form encapsulation, generates forms through configuration.

TIP

If form item components support placeholder, clearable, filterable, these attributes are configured by default

Basic Usage

  • Pass columns to define the form, modelValue for form data, options for data option sources, component for form item component, field for field name, fieldProps for component attribute configuration, formItemProps for form item decorator component attribute configuration
  • FormItem component attributes (form item decorator component) are configured in the formItemProps field. For convenience, some fields can also take effect when configured directly in the column item (e.g.: label, required, message, rule, etc.)
  • Events use on + event name
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: ElInput,
    field: 'name',
    label: 'Name',
    onInput: (val: string) => {
      console.log(val, 'input event')
    },
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    onFocus: () => {
      console.log('focus event')
    },
  },
  {
    component: 'el-date-picker',
    field: 'time',
    label: 'Date',
    fieldProps: {
      type: 'daterange',
      startPlaceholder: 'Start date',
      endPlaceholder: 'End date',
    },
    onChange: (val: string[]) => {
      console.log(val, 'change event')
    },
  },
  {
    slot: 'button',
  },
]

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          Reset
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          Submit
        </el-button>
      </div>
    </template>
  </z-form>
</template>

Column Layout

Pass column to the form to support 1 ~ 3 column layout, default is 1 column layout.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
  },
  {
    component: 'input',
    label: 'Age',
    field: 'age',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Custom Layout

If the column layout does not meet the requirements, you can configure span, offset, pull, push, xs, sm, md, lg, xl in the column item to achieve a custom responsive layout

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    required: true,
    span: 24,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    span: 12,
  },
  {
    component: 'input',
    label: 'Age',
    field: 'age',
    span: 12,
  },
  {
    span: 24,
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Tooltip

Pass tooltip, extra, and colon in column to configure tooltip, extra info, and colon. tooltip and extra support string and render function. The form can pass colon to uniformly configure colons for all form items.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    tooltip: 'Name tooltip',
    extra: 'extraSlot',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    colon: false,
    tooltip: () => h('span', {}, 'Gender hint'),
    required: true,
  },
  {
    component: 'input',
    label: 'Age',
    field: 'age',
    formItemProps: {
      tooltip: 'Age tooltip',
      extra: () => h('span', {}, 'This is extra info'),
      colon: false,
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
    colon
  >
    <template #extraSlot>
      <span>Hint hint hint hint hint hint hint hint hint hint hint hint hint hint hint hint hint hint hint</span>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

tooltip supports passing a render function and slot customization.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    tooltip: {
      reference: () => h('span', {}, '?'),
      content: 'Name hint',
    },
    extra: 'extraSlot',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    colon: false,
    tooltip: () => h('span', {}, 'Gender hint'),
    required: true,
  },
  {
    component: 'input',
    label: 'Age',
    field: 'age',
    formItemProps: {
      tooltip: {
        reference: 'tooltipSlot',
        content: () => h('span', {}, 'Custom age hint'),
      },
      colon: false,
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
    colon
  >
    <template #tooltipSlot>
      <el-icon class="cursor-pointer">
        <i-delete />
      </el-icon>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Validation

  • Add required in columns form items, or set required in formItemProps to make it required. The validation message will be automatically generated based on label or can be customized.
  • Pass rules in form to define form validation rules.
  • Configure rules in columns form items to define the validation rules for the current form item.
<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
  hobby: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const rules = {
  age: [{ required: true, message: 'Age is required' }],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    formItemProps: {
      required: true,
      label: 'Gender',
    },
  },
  {
    component: 'input',
    label: 'Age',
    field: 'age',
  },
  {
    component: 'input',
    label: 'Hobby',
    field: 'hobby',
    rules: {
      required: true,
      message: 'Please enter a hobby',
    },
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :rules="rules"
    :column="3"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Linkage

Pass hide (supports boolean or function type) to column form items to configure linkage hide/show for form items.

Some business scenarios require using v-show to determine component visibility. Pass the show field (supports boolean or function type) to column form items.

If the name input has a value, the gender select box will show. Then try selecting gender; the age form item will appear.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    show: (formData: any) => formData.name,
  },
  {
    component: 'input',
    label: 'Age',
    hide: formData => formData.gender === '1' || !formData.gender,
    field: 'age',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="90px"
    size="small"
    :column="2"
  >
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Custom Form Items

We can use slot or render to customize form item content. Please still pass the field field; otherwise, reset cannot clear the data (field not passed will default to slot).

<script lang="ts" setup>
import { h, ref } from 'vue'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: 'Name',
    required: true,
  },
  {
    slot: 'gender',
    field: 'gender',
    label: 'Gender',
  },
  {
    label: 'Age',
    render: () => h('span', {}, 'Age details'),
  },
]
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :columns="columns"
    label-width="90px"
    size="small"
  >
    <template #gender>
      <z-select v-model="formData.gender" :options="options.gender" clearable filterable placeholder="Please select a gender" />
    </template>
  </z-form>
</template>

Custom label and error

label and error support strings, render functions, or concatenated Slot strings.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { h, ref } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  age: '',
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'input',
    field: 'name',
    label: () => h('span', {}, 'Name'),
    error: 'error message',
    required: true,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'labelSlot',
    required: true,
    error: h('span', {}, 'errorSlot'),
  },
  {
    component: 'input',
    field: 'age',
    label: 'Age',
    required: true,
    error: 'errorSlot',
  },
  {
    slot: 'button',
  },
]

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      ElMessage.success('success')
      console.log(formData.value, 'formData.value')
    }
    else {
      console.log('error')
    }
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :columns="columns"
    :options="options"
    label-width="90px"
    size="small"
  >
    <template #labelSlot>
      <span>Gender</span>
    </template>
    <template #errorSlot>
      <span>Age is required</span>
    </template>
    <template #button>
      <el-button class="w-full" type="primary" @click="submit">
        Submit
      </el-button>
    </template>
  </z-form>
</template>

Form Item Order

Pass order to form items to support custom order. If not passed, it defaults to placing at the end in order.

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'
import { ElInput } from 'element-plus'

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = [
  {
    component: 'el-input',
    field: 'name',
    label: 'Address',
  },
  {
    component: ElInput,
    field: 'name',
    label: 'Name',
    order: 2,
  },
  {
    component: 'select',
    field: 'gender',
    label: 'Gender',
    order: 3,
  },
  {
    component: 'el-date-picker',
    field: 'time',
    label: 'Date',
    order: 1,
  },
  {
    slot: 'button',
  },
]

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          Reset
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          Submit
        </el-button>
      </div>
    </template>
  </z-form>
</template>

Combine with Table

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import { ref } from 'vue'

interface RowData {
  name: string
  address: string
}

const formRef = ref()
const formData = ref({
  name: '',
  gender: '',
  time: [],
  tableData: [{ name: 'steven', address: '' }],
})

const options = {
  gender: [
    { label: 'Male', value: '1' },
    { label: 'Female', value: '2' },
  ],
}

const columns = computed(() => [
  {
    component: 'el-input',
    field: 'name',
    label: 'Name',
    onInput: (val: string) => {
      console.log(val, 'input event')
    },
    onChange: (val: string) => {
      console.log(val, 'change event')
    },
    required: true,
  },
  {
    'component': 'z-table',
    'field': 'tableData',
    'label': 'Table',
    'onUpdate:data': (data: RowData[]) => {
      console.log(data, 'data')
      formData.value.tableData = data
    },
    'fieldProps': {
      data: formData.value.tableData,
      toolBar: false,
      columns: [
        {
          label: 'Name',
          prop: 'name',
        },
        {
          component: 'el-input',
          label: 'Address',
          prop: 'address',
        },
      ],
    },
  },
  {
    slot: 'button',
  },
])

function reset() {
  formRef.value.resetFields()
}

function submit() {
  formRef.value.validate((valid: boolean) => {
    if (valid)
      console.log(formData.value, 'config.formData')

    else
      console.log('error')
  })
}
</script>

<template>
  <z-form
    ref="formRef"
    v-model="formData"
    :options="options"
    :columns="columns"
    label-width="80px"
    size="small"
  >
    <template #button>
      <div class="w-full flex">
        <el-button class="w-full" @click="reset">
          Reset
        </el-button>
        <el-button type="primary" class="w-full" @click="submit">
          Submit
        </el-button>
      </div>
    </template>
  </z-form>
</template>

z-form Attributes

AttributeDescriptionTypeDefault
modelValueForm data objectobject
typeForm typenormal / group / collapse / array / stepnormal
rulesForm validation rulesobject
columnsForm itemsarray
optionsData source of form optionsobject
columnNumber of form columns (convenient layout)number1
colonColon of form itembooleanfalse
maxMaximum number of array items (effective when type is array)number
gutterGrid spacingnumber0
justifyHorizontal alignment under flex layoutstart / end / center / space-around / space-between / space-evenly
alignVertical alignment under flex layouttop / middle /bottom
v-model:activeCollapseExpanded collapse items (effective when type is collapse)array
label-positionPosition of form field labels. When set to left or right, you also need to set label-widthenumright
label-widthLabel width, e.g. '50px'. Form-items that are direct children of Form inherit this value. auto can be used.string / number''
label-suffixSuffix of the form field labelstring''
hide-required-asteriskWhether to hide the red asterisk next to required field labelsbooleanfalse
require-asterisk-positionPosition of the asteriskleft / rightleft
show-messageWhether to display validation error messagesbooleantrue
inline-messageWhether to display validation messages inlinebooleanfalse
status-iconWhether to display validation result feedback icons in the input boxbooleanfalse
validate-on-rule-changeWhether to trigger a validation immediately after the rules attribute changesbooleantrue
sizeSize used to control the components within this formlarge / default / small
disabledWhether to disable all components in this form. If set to true, it overrides internal components' disabledbooleanfalse
scroll-to-errorScroll to the first erroneous form item when validation failsbooleanfalse
scroll-into-view-optionsWhen there are failed validation results, scroll to the first failed form itemobject / boolean

group Form Attributes

AttributeDescriptionTypeDefault
border-styleSet separator stylenone / solid / hidden / dashedsolid
content-positionPosition of the separator contentleft / right / centercenter

array Form Attributes

AttributeDescriptionTypeDefault
maxMaximum number of form itemsnumber

step Form Attributes

AttributeDescriptionTypeDefault
process-statusSet the status of the current stepwait / process / finish / error / successprocess
finish-statusSet the status of the finish stepwait / process / finish / error / successfinish
align-centerCenter alignmentbooleantrue

z-form Methods

NameDescriptionType
validateValidate the entire form. Accepts a callback function or returns a Promise.Function
validateFieldValidate a specific field.Function
resetFieldsReset this form item, restore its value to the initial value, and remove the validation resultFunction
scrollToFieldScroll to the specified fieldFunction
clearValidateClear the validation information of a certain field.Function

z-form Events

Event NameDescriptionType
update:modelValueForm item dataFunction
update:activeCollapseExpanded items of collapse formFunction
update:activeStepCurrent step of step formFunction
collapse-changeTriggered when collapse changesFunction
next-stepTriggered when clicking next stepFunction
previous-stepTriggered when clicking prev stepFunction

Common Form Column Attributes

AttributeDescriptionTypeDefault
componentForm item componentinput / checkbox / select / radio / Any locally or globally registered component
fieldField namestring
fieldPropscomponent attributesobject
formItemPropsformItem attributesobject
labelForm label namestring / () => VNode
orderForm ordernumber
hideShow / hideboolean / (formData) => boolean
showShow / hide using v-showboolean / (formData) => boolean
slotCustom content slot of form itemstring
renderCustom content render of form item() => VNode
requiredWhether the field is requiredboolean
rulesValidation rules of the fieldboolean
errorError messagestring / () => VNode
tooltipTooltip messagestring / () => VNode
extraExtra informationstring / () => VNode
childrenEffective in form collapse mode, column arrayarray
spanNumber of grid spaces occupiednumber
offsetNumber of grid spaces to the leftnumber
pullNumber of grid spaces moved to the leftnumber
pushNumber of grid spaces moved to the rightboolean
xs<768px responsive grid or grid props objectnumber / object
sm≥768px responsive grid or grid props objectnumber / object
md≥992px responsive grid or grid props objectnumber / object
lg≥1200px responsive grid or grid props objectnumber / object
xl≥1920px responsive grid or grid props objectnumber / object

array Form Column Attributes

AttributeDescriptionTypeDefault
labelLabel namestring / () => VNode
fieldField namestring
maxMaximum number of form itemsnumber
childrenForm items in the current step (all are common column attributes)array

step Form Column Attributes

AttributeDescriptionTypeDefault
labelStep titlestring / () => VNode
iconIcon for step modestring / Component
descriptionDescription for step modeboolean
statusStep status for step modewait / process / finish / error / success
childrenForm items in the current step (all are common column attributes)array

formItemProps Attributes

AttributeDescriptionTypeDefault
tooltipTooltip textstring / () => VNode
extraExtra informationstring / () => VNode
colonColonboolean
labelLabel textstring / () => VNode
label-widthLabel width, e.g. '50px'. auto can be used.string / number''
requiredWhether it is required. If not set, it will be confirmed according to the validation rulesboolean
rulesForm validation rules. See table below; more at async-validatorobject
errorPrompt information when the form field validation fails. Setting this value will cause the form validation state to become error and display this error message.string / () => VNode
show-messageWhether to display validation error messagesbooleantrue
inline-messageWhether to display validation messages inlinestring / boolean''
sizeDefault size of components under this form fieldlarge / default / small
forSame capability as the native labelstring
validate-statusValidation status of formItemerror / validating / success

Released under the MIT License.