Custom Form
Custom Form, using useFormComponentLogics
with Ant Design UI
Basic
To define a Form Component, there are three things we need to care:
- props
- emitter
- logics
About props, EzForm provide function getFormDefinePropsObject
, let you define props easier. Then emit, you can define it by copy it from this article.
EzForm also provide a composable called useFormComponentLogics
which receive props
and emitter
as parameters. This composable will handle almost logics for your Form
. It will return a FormInstance
and two functions called submit
and reset
. You can use them in template.
Now, let's see how our form component look like when be written with getFormDefinePropsObject
and useFormComponentLogics
.
<!-- AntForm.vue -->
<template>
<form @submit.prevent="submit()" @reset.prevent="reset()">
<slot
:values="meta.values"
:errors="meta.errors"
:dirty="meta.dirty"
:submit="submit"
:reset="reset"
:validate="validate"
:getFieldValue="getFieldValue"
:setFieldValue="setFieldValue"
:isDirty="isDirty"
/>
</form>
</template>
<script lang="ts" setup>
import {
getFormDefinePropsObject,
useFormComponentLogics,
ValidateError,
} from "@niku/ez-form";
export interface FormEmitter {
(event: "submit", values: any): void;
(event: "change", values: any): void;
(event: "reset"): void;
(event: "error", errors: ValidateError[]): void;
}
const props = defineProps(getFormDefinePropsObject());
const emit = defineEmits<FormEmitter>();
const { submit, reset, formInstance } = useFormComponentLogics(props, emit);
const { meta, setFieldValue, getFieldValue, validate, isDirty } = formInstance;
</script>
So short, right? EzForm has done almost things. You just need to care about UI. How to use Ant Design? Let's see in next step.
Add Ant Design Style
Form classes
Just add class ant-form
to our tag form
.
<!-- AntForm.vue -->
<template>
...
<form class="ant-form" ... />
...
</template>
<script lang="ts" setup>
...
</script>
Customizable layout
Ant Design let's us customize form layout by changing props. So, we will let our component can do the same.
Luckily, Ant Design also provide function formProps
, so we can merge our props in previous step with Ant Design's props.
We will pick four props from formProps
:
- layout
- labelCol
- labelAlign
- wrapperCol
<!-- AntForm.vue -->
<template>
<form :class="['ant-form', `ant-form-${layout ?? 'vertical'}`]" ... />
</template>
<script lang="ts" setup>
import { formProps } from "ant-design-vue/es/form";
...
const props = defineProps({
...getFormDefinePropsObject(),
labelCol: formProps()["labelCol"],
labelAlign: formProps()["labelAlign"],
wrapperCol: formProps()["wrapperCol"],
layout: formProps()["layout"],
});
...
</script>
As you can see, form
's property class
changed to :class="['ant-form', `ant-form-${layout ?? 'vertical'}`]"
. That is prop layout
, how about remain.
We will provide them, then use in custom FormItem
.
Firstly, we need define a inject key and an interface.
// useInjectAntFormStyle.ts
import { ColProps } from "ant-design-vue";
import { FormLabelAlign } from "ant-design-vue/lib/form/interface";
import { inject } from "vue";
export const $injectFormStyleKey = Symbol("injectFormStyleKey");
export interface InjectFormStyle {
labelCol?: ColProps;
labelAlign?: FormLabelAlign;
wrapperCol?: ColProps;
}
Then, use them in our component file.
<!-- AntForm.vue -->
...
<script lang="ts" setup>
import {provide} from "vue";
import { $injectFormStyleKey, InjectFormStyle } from "./useInjectAntFormStyle";
...
provide<InjectFormStyle>($injectFormStyleKey, {
get labelCol() {
return props.labelCol;
},
get labelAlign() {
return props.labelAlign;
},
get wrapperCol() {
return props.wrapperCol;
},
});
</script>
That's all, our AntForm
is completed.
Full source
// useInjectAntFormStyle.ts
import { ColProps } from "ant-design-vue";
import { FormLabelAlign } from "ant-design-vue/lib/form/interface";
import { inject } from "vue";
export const $injectFormStyleKey = Symbol("injectFormStyleKey");
export interface InjectFormStyle {
labelCol?: ColProps;
labelAlign?: FormLabelAlign;
wrapperCol?: ColProps;
}
<!-- AntForm.vue -->
<template>
<form
:class="['ant-form', `ant-form-${layout ?? 'vertical'}`]"
@submit.prevent="submit()"
@reset.prevent="reset()"
>
<slot
:values="meta.values"
:errors="meta.errors"
:dirty="meta.dirty"
:submit="submit"
:reset="reset"
:validate="validate"
:getFieldValue="getFieldValue"
:setFieldValue="setFieldValue"
:isDirty="isDirty"
/>
</form>
</template>
<script lang="ts" setup>
import { formProps } from "ant-design-vue/es/form";
import { provide } from "vue";
import {
getFormDefinePropsObject,
useFormComponentLogics,
ValidateError,
} from "@niku/ez-form";
import { $injectFormStyleKey, InjectFormStyle } from "./useInjectAntFormStyle";
export interface FormEmitter {
(event: "submit", values: any): void;
(event: "change", values: any): void;
(event: "reset"): void;
(event: "error", errors: ValidateError[]): void;
}
const props = defineProps({
...getFormDefinePropsObject(),
labelCol: formProps()["labelCol"],
labelAlign: formProps()["labelAlign"],
wrapperCol: formProps()["wrapperCol"],
layout: formProps()["layout"],
});
const emit = defineEmits<FormEmitter>();
const { submit, reset, formInstance } = useFormComponentLogics(props, emit);
const { meta, setFieldValue, getFieldValue, validate, isDirty } = formInstance;
provide<InjectFormStyle>($injectFormStyleKey, {
get labelCol() {
return props.labelCol;
},
get labelAlign() {
return props.labelAlign;
},
get wrapperCol() {
return props.wrapperCol;
},
});
</script>
Result
The final result when you have done Custom FormItem
and Custom FormList
.
View Code
<template>
<div class="example-box">
<AntForm @submit="handleSubmit" @reset="handleReset">
<AntFormItem label="Username" name="username">
<a-input />
</AntFormItem>
<AntFormItem label="Password" name="password">
<a-input-password />
</AntFormItem>
<a-space>
<a-button html-type="submit" type="primary"> Submit </a-button>
<a-button html-type="reset" type="default"> Reset </a-button>
</a-space>
</AntForm>
</div>
</template>
<script lang="ts" setup>
import AntForm from "./AntForm.vue";
import AntFormItem from "./AntFormItem.vue";
const handleSubmit = (values: any) => {
console.log("Form submit", values);
};
const handleReset = () => {
console.log("Form reset");
};
</script>