Master Vue 3 Form Components: Build, Validate, and Communicate Efficiently
This article explains how to create a robust Vue 3 form component using Element Plus, covering component hierarchy, data binding with model, rule definition with async-validator, provide/inject communication, and step‑by‑step validation logic with practical code examples.
Preface
The previous article introduced Jest testing for component libraries; now we focus on a form component that not only renders UI but also handles rich interactions, starting from Element Plus form components.
Form Component Overview
Element Plus provides several form‑related components such as el-form (the outer container), el-form-item (label and validation management), and input widgets like el-input or el-switch. The typical template consists of three layers: el-form – the form container. el-form-item – manages each field’s label and validation. el-input / el-switch – the actual input controls.
<el-form :model="ruleForm" :rules="rules" ref="form" label-width="100px">
<el-form-item label="Activity Name" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
...
</el-form>A simplified version that only keeps el-input demonstrates the basic data flow: el-form uses :model for data binding and :rules for validation. el-form-item wraps the input, performs validation, and displays error messages.
<el-form :model="ruleForm" :rules="rules" ref="form">
<el-form-item label="Username" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="Password" prop="passwd">
<el-input type="textarea" v-model="ruleForm.passwd"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm()">Login</el-button>
</el-form-item>
</el-form>Rules and Model Workflow
We create a reactive ruleForm object for user input and a reactive rules object that follows the async-validator schema. Validation is triggered via the form’s validate method, which displays error messages when a rule fails.
const ruleForm = reactive<UserForm>({
username: "",
passwd: ""
});
// Define validation rules
const rules = reactive({
rules: {
username: { required: true, min: 1, max: 20, message: 'Length 1‑20', trigger: 'blur' },
passwd: [{ required: true, message: 'Password required', trigger: 'blur' }]
}
});
function submitForm() {
form.value.validate(valid => {
if (valid) alert('submit!');
else console.log('error submit!!');
});
}Implementing the Form Component
In src/components/Form.vue we register props ( label, prop) and expose a validate method so the parent form can trigger validation on each item.
interface Props {
label?: string;
prop?: string;
}
const props = withDefaults(defineProps<Props>(), { label: "", prop: "" });
const formData = inject(key);
const o: FormItem = { validate };
defineExpose(o);Each FormItem.vue registers itself with the parent form via an event emitter:
import { emitter } from "../../emitter";
const items = ref<FormItem[]>([]);
emitter.on("addFormItem", item => {
items.value.push(item);
});During onMounted, if the component has a prop, it subscribes to a global validate event and notifies the parent:
onMounted(() => {
if (props.prop) {
emitter.on("validate", () => validate());
emitter.emit("addFormItem", o);
}
});
function validate() {
if (!formData?.rules) return Promise.resolve({ result: true });
const schema = new Schema({ [props.prop]: formData.rules[props.prop] });
return schema.validate({ [props.prop]: formData.model[props.prop] }, errors => {
error.value = errors ? errors[0].message : "";
});
}The relationship among el-form, el-form-item, and input components is a nested hierarchy: the form provides the data model and rules, the input handles user interaction, and the form‑item manages validation and error display.
Component Communication
Vue 3’s provide / inject API replaces the older event‑bus pattern. The form provides a context object containing model and rules:
provide(key, { model: props.model, rules: props.rules });
const formData = inject(key);Child components obtain this context via inject, ensuring type safety with an InjectionKey:
import { InjectionKey } from "vue";
import { Rules, Values } from "async-validator";
export type FormData = { model: Record<string, unknown>; rules?: Rules };
export type FormItem = { validate: () => Promise<Values> };
export const key: InjectionKey<FormData> = Symbol("form-data");Parent‑Child Communication
Props pass data downwards, while emit (or the event‑bus) notifies the parent of changes. In Vue 3, provide/inject combined with reactive data offers a clearer, type‑safe data flow.
FAQ – Event Bus vs. Provide/Inject
Using an event‑bus in Vue 2 leads to loose coupling, potential performance overhead, and difficult debugging. Modern Vue 3 prefers provide/inject or state‑management libraries (Pinia, Vuex) for scoped, efficient communication. The example demonstrates how provide/inject simplifies registration, lifecycle handling, and validation coordination.
Form Validation Process
Define validation rules (e.g., required, min/max, custom validator).
Bind the rules to el-form via the :rules attribute.
Optionally implement custom validator functions.
Trigger validation (e.g., on submit) and handle the result.
When validation fails, error messages appear next to the corresponding input; on success, the form can be submitted to the backend.
Summary
The article details the design and implementation of a complex Vue 3 form component, covering data binding, rule definition with async-validator, component hierarchy, and communication via provide/inject. It also compares the legacy event‑bus approach with modern patterns, recommending the latter for maintainable, performant code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaEdge
First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
