Form
Basic Usage
Building a form using UniForm
and UniFormItem
is incredibly straightforward.
Internally, the form utilizes async-validator
to validate the data entered into it. You can set validation rules for each form item using the rules
property. For more information regarding validation rules, please refer to the async-validator GitHub page.
View Source
<script setup lang="ts">
import { UniForm, UniFormItem, UniInput, UniButton, UniPassword, UniSelect, UniSelectOption } from "unify-ui";
import { ref } from "vue";
const model = ref({
username: "",
password: "",
address: "",
gender: "",
});
const formRef = ref<InstanceType<typeof UniForm> | null>(null);
async function handleSubmit() {
try {
await formRef.value?.validate();
console.log("validate pass, model:", model);
} catch (error) {
console.error("validate error");
}
}
function handleReset() {
formRef.value?.resetFields();
}
</script>
<template>
<UniForm ref="formRef" v-model:model="model">
<UniFormItem name="username" label="Username" :rules="[{ required: true, message: 'Username is required' }]">
<UniInput v-model="model.username" />
</UniFormItem>
<UniFormItem name="password" label="Password" :rules="[{ required: true, message: 'Password is required' }]">
<UniPassword v-model="model.password" />
</UniFormItem>
<UniFormItem name="address" label="Address" :rules="[{ required: true, message: 'Address is requried' }]">
<UniInput v-model="model.address" />
</UniFormItem>
<UniFormItem name="gender" label="Geneder" :rules="[{ required: true, message: 'Gender is requried' }]">
<UniSelect v-model="model.gender">
<UniSelectOption label="male" value="male" />
<UniSelectOption label="female" value="Female" />
</UniSelect>
</UniFormItem>
<div>
<UniButton class="button" type="soft" @click="handleSubmit">Submit</UniButton>
<UniButton class="button" @click="handleReset">Reset</UniButton>
</div>
</UniForm>
</template>
<style scoped>
.button {
width: 100px;
margin-right: 20px;
}
</style>
Customized Form Control
In some cases, you might need to use a custom form control not provided by Unify UI. The appearance of this customized form control may behave unexpectedly when the value is invalid. For instance, you may want the custom form control to display a red border when the value is invalid. There are two methods to achieve this.
The first method involves using UniFormItem
, which provides Scoped Slots with a validateStatus
state. When the form control's value is invalid, validateStatus is set to error
, and when the value is valid, it is set to success
. You can use this to customize the appearance of the form control:
View Source
<script setup lang="ts">
import { UniForm, UniFormItem, UniButton } from "unify-ui";
import { ref } from "vue";
const model = ref({
username: "",
});
const formRef = ref<InstanceType<typeof UniForm> | null>(null);
async function handleSubmit() {
try {
await formRef.value?.validate();
console.log("validate pass, model:", model);
} catch (error) {
console.error("validate error");
}
}
function handleReset() {
formRef.value?.resetFields();
}
</script>
<template>
<UniForm ref="formRef" v-model:model="model">
<UniFormItem
name="username"
:rules="[{ required: true, message: 'Username is required' }]"
label="Username"
>
<template #default="slotProps">
<input
v-model="model.username"
class="input"
:class="{ 'input--error': slotProps.validateStatus === 'error' }"
/>
</template>
</UniFormItem>
<div>
<UniButton class="button" type="soft" @click="handleSubmit">Submit</UniButton>
<UniButton class="button" @click="handleReset">Reset</UniButton>
</div>
</UniForm>
</template>
<style scoped>
.input {
border: 1px solid black;
}
.input--error {
border: 1px solid red;
}
.button {
width: 100px;
margin-right: 20px;
}
</style>
The second method involves using the composable function useGetFormItemValidateStatus
, which provides access to the validateStatus
state. It's important to mention that when using this method, you need to wrap the form control with UniFormItem
because the validateStatus
state is provided by UniFormItem
using the provide
API:
View Source
Source code of customized input:
<script setup lang="ts">
import { useGetFormItemValidateStatus } from "unify-ui";
const validateStatus = useGetFormItemValidateStatus();
</script>
<template>
<input v-bind="$attrs" class="input" :class="{ 'input--error': validateStatus === 'error' }" />
</template>
<style scoped>
.input {
border: 1px solid black;
}
.input--error {
border: 1px solid red;
}
</style>
Source code of form example:
<script setup lang="ts">
import { UniForm, UniFormItem, UniButton } from "unify-ui";
import { ref } from "vue";
import CustomizedInput from "./ShowCaseCustomizedFormControl2Input.vue";
const model = ref({
username: "",
});
const formRef = ref<InstanceType<typeof UniForm> | null>(null);
async function handleSubmit() {
try {
await formRef.value?.validate();
console.log("validate pass, model:", model);
} catch (error) {
console.error("validate error");
}
}
function handleReset() {
formRef.value?.resetFields();
}
</script>
<template>
<UniForm ref="formRef" v-model:model="model">
<UniFormItem name="username" :rules="[{ required: true, message: 'Username is required' }]" label="Username">
<CustomizedInput v-model="model.username" />
</UniFormItem>
<div>
<UniButton class="button" type="soft" @click="handleSubmit">Submit</UniButton>
<UniButton class="button" @click="handleReset">Reset</UniButton>
</div>
</UniForm>
</template>
<style scoped>
.button {
width: 100px;
margin-right: 20px;
}
</style>
Layout
The form layout can be set using the layout
property. The default value is vertical
.
View Source
<script setup lang="ts">
import { UniForm, UniFormItem, UniInput, UniButton, UniPassword, UniRadioGroup } from "unify-ui";
import { ref } from "vue";
const model = ref({
username: "",
password: "",
address: "",
});
const formRef = ref<InstanceType<typeof UniForm> | null>(null);
async function handleSubmit() {
try {
await formRef.value?.validate();
console.log("validate pass, model:", model);
} catch (error) {
console.error("validate error");
}
}
function handleReset() {
formRef.value?.resetFields();
}
const options = ["horizontal", "vertical", "inline"].map((item) => ({ label: item, value: item }));
const layout = ref<InstanceType<typeof UniForm>["$props"]["layout"]>("horizontal");
</script>
<template>
<div class="container">
<UniRadioGroup
v-model="layout"
:options="options"
name="layout-options"
inline
:style="{ marginBottom: '20px' }"
></UniRadioGroup>
<UniForm ref="formRef" v-model:model="model" :layout="layout" label-width="90px">
<UniFormItem
name="username"
:rules="[{ required: true, message: 'Username is required' }]"
label="Username"
>
<UniInput v-model="model.username" class="input" />
</UniFormItem>
<UniFormItem
name="password"
:rules="[{ required: true, message: 'Password is required' }]"
label="Password"
>
<UniPassword v-model="model.password" class="input" />
</UniFormItem>
<UniFormItem name="address" label="Address">
<UniInput v-model="model.address" class="input" />
</UniFormItem>
<UniFormItem>
<UniButton class="button" type="soft" @click="handleSubmit">Submit</UniButton>
<UniButton class="button" @click="handleReset">Reset</UniButton>
</UniFormItem>
</UniForm>
</div>
</template>
<style scoped>
.container {
padding: 20px;
}
.button {
width: 100px;
margin-right: 20px;
}
.input {
width: 150px;
}
</style>
Form API
Properties
export interface FormProps {
model: Model;
layout?: "horizontal" | "vertical" | "inline";
labelWidth?: string;
}
const props = withDefaults(defineProps<FormProps>(), {
layout: "vertical",
});
Events
const emits = defineEmits<{
(e: "update:model", value: Model): void;
}>();
Slots
defineSlots<{
default(props: {}): any;
}>();
Expose
defineExpose({
validate, // trigger data entry validation
clearValidate, // clear data entry validation,
resetFields, // reset data and clear validation
});
FormItem API
Properties
import type { RuleItem } from "async-validator";
const props = withDefaults(
defineProps<{
label?: string;
name?: string;
rules?: RuleItem[];
}>(),
{
label: undefined,
name: undefined,
rules: () => [],
}
);
Slots
export type ValidateStatus = "success" | "error" | undefined;
defineSlots<{
default(props: { validateStatus: ValidateStatus }): any;
}>();