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.