Forms
VEF form capabilities mainly come from @vef-framework-react/components, and the two main entry points are:
useForm()useFormContext()
It can be understood as an enterprise-oriented UI wrapper around TanStack Form.
Typical Usage
import { Grid, useDataOptionsSelect, useFormContext } from "@vef-framework-react/components";
import { z } from "@vef-framework-react/shared";
const validators = {
name: z.string("Required").min(2, "At least 2 characters"),
gender: z.string("Required")
};
export function UserForm() {
const { AppField } = useFormContext<{ name: string; gender: string }>();
const genderSelectProps = useDataOptionsSelect({
dataDictKey: "common.gender"
});
return (
<Grid columnGap="small">
<Grid.Item span={12}>
<AppField name="name" validators={{ onBlur: validators.name }}>
{field => <field.Input required label="Name" />}
</AppField>
</Grid.Item>
<Grid.Item span={12}>
<AppField name="gender" validators={{ onChange: validators.gender }}>
{field => <field.Select {...genderSelectProps} required label="Gender" />}
</AppField>
</Grid.Item>
</Grid>
);
}
useForm() Creates the Form Instance
import { useForm } from "@vef-framework-react/components";
const {
Form,
AppField,
SubmitButton
} = useForm({
defaultValues: {
keyword: ""
},
onSubmit({ value }) {
console.log(value);
}
});
useFormContext() Works Well for Split Forms
When a form is split into separate components, a new form instance is usually not needed. AppField can be retrieved from context instead:
const { AppField } = useFormContext<FormValues>();
Validation with z
import { z } from "@vef-framework-react/shared";
const validators = {
username: z.string("Required").min(2, "At least 2 characters").max(16, "At most 16 characters"),
email: z.email().nullish()
};
Feeding Data into Option Fields
VEF applications usually avoid fetching inside field components. Instead, hooks generate props that can be spread directly into controls.
Select
const roleSelectProps = useDataOptionsSelect({
filterable: true,
queryOptions: {
queryKey: [findRoleOptions.key],
queryFn: findRoleOptions
}
});
Tree Select
const deptTreeSelectProps = useDataOptionsTreeSelect({
filterable: true,
queryOptions: {
queryKey: [findDepartmentTree.key],
queryFn: findDepartmentTree
}
});
Data Dictionary
const genderSelectProps = useDataOptionsSelect({
dataDictKey: "common.gender"
});
Common Field Types
field.Inputfield.Passwordfield.TextAreafield.Selectfield.TreeSelectfield.Boolfield.Uploadfield.DatePickerfield.InputNumber
It is usually better to keep data sourcing and field rendering separate:
useDataOptionsSelect()fetches or builds optionsAppFieldbinds field stateGridhandles layout