@fidt/dynamic-form
v1.0.1
Published
A dynamic form library for Vue 3 with built-in validation support (Zod, ArkType)
Readme
@fidt/dynamic-form
Thư viện dynamic form cho Vue 3 với hỗ trợ validation tích hợp (Zod, ArkType).
✨ Tính năng
- 🎯 Dynamic Form Generation - Tạo form từ schema JSON
- 📐 Flexible Layout - Hỗ trợ grid layout đa cột và nested fields
- ✅ Built-in Validation - Validation tích hợp với Zod/ArkType
- 🎨 UI Component Agnostic - Tương thích với bất kỳ UI component nào
- 📦 Type-safe - Fully typed với TypeScript
- 🔄 Reactive - Tích hợp Vue reactivity system
📦 Cài đặt
npm install @fidt/dynamic-form
# hoặc
pnpm add @fidt/dynamic-form🚀 Sử dụng cơ bản
1. Import và đăng ký
import DymikForm from '@fidt/dynamic-form'
import '@fidt/dynamic-form/dist/dynamic-form.css'
// Đăng ký global (optional)
app.use(DymikForm)
// Hoặc import trực tiếp trong component
import { DynamicForm, FormModel } from '@fidt/dynamic-form'2. Tạo form đơn giản
<script setup lang="ts">
import { reactive } from 'vue'
import { DynamicForm, FormModel } from '@fidt/dynamic-form'
const form = reactive(
new FormModel({
name: 'contact-form',
description: 'Form liên hệ đơn giản',
fields: [
{
name: 'fullName',
label: 'Họ và tên',
type: 'FInput',
required: true,
required_text: 'Vui lòng nhập họ tên',
props: { placeholder: 'Nhập họ và tên' },
validation_rules: [
{ type: 'minLength', value: 2, message: 'Tên phải có ít nhất 2 ký tự' }
]
},
{
name: 'email',
label: 'Email',
type: 'FInput',
required: true,
props: { placeholder: '[email protected]' },
validation_rules: [
{ type: 'email', message: 'Email không hợp lệ' }
]
},
{
name: 'submit',
type: 'FButton',
role: 'submit',
props: { label: 'Gửi', order: 'primary' }
}
]
})
)
function onSubmit(values: Record<string, unknown>) {
console.log('Form data:', values)
}
</script>
<template>
<DynamicForm :form="form" @submit="onSubmit" />
</template>📐 Layout nâng cao
Grid Layout với nhiều cột
const form = reactive(
new FormModel({
name: 'registration-form',
columns: 3, // Số cột trong grid
layout: [
['fullName', 'email', 'phone'], // Hàng 1: 3 fields
[['idCard', 'issueDate'], 'address', null], // Hàng 2: nested fields, 1 field, 1 ô trống
[null, null, 'submit'], // Hàng 3: button căn phải
],
fields: {
fullName: {
label: 'Họ và tên',
type: 'FInput',
required: true,
props: { placeholder: 'Nhập họ tên' }
},
email: {
label: 'Email',
type: 'FInput',
required: true,
validation_rules: [{ type: 'email', message: 'Email không hợp lệ' }]
},
phone: {
label: 'Số điện thoại',
type: 'FInput',
props: { placeholder: '0123456789' }
},
idCard: {
label: 'CCCD',
type: 'FInput',
required: true,
props: { placeholder: 'Số CCCD' }
},
issueDate: {
label: 'Ngày cấp',
type: 'FInput',
props: { type: 'date' }
},
address: {
label: 'Địa chỉ',
type: 'FInput',
props: { placeholder: 'Địa chỉ hiện tại' }
},
submit: {
type: 'FButton',
role: 'submit',
props: { label: 'Đăng ký', order: 'primary' }
}
}
})
)Giải thích Layout
columns: Số cột trong grid (mặc định: auto)layout: Mảng 2D định nghĩa vị trí các field'fieldName'- Field đơn chiếm 1 ô['field1', 'field2']- Nhiều field nested trong 1 ônull- Ô trống (dùng để căn chỉnh)
Format Fields
Có 2 cách định nghĩa fields:
1. Array format (truyền thống):
fields: [
{
name: 'email',
label: 'Email',
type: 'FInput',
// ...
}
]2. Object format (recommended với layout):
fields: {
email: {
label: 'Email',
type: 'FInput',
// ...
}
// name tự động lấy từ key
}✅ Validation
Các loại validation được hỗ trợ
validation_rules: [
// String validation
{ type: 'string', message: 'Phải là chuỗi' },
{ type: 'minLength', value: 3, message: 'Tối thiểu 3 ký tự' },
{ type: 'maxLength', value: 50, message: 'Tối đa 50 ký tự' },
// Number validation
{ type: 'number', message: 'Phải là số' },
{ type: 'min', value: 18, message: 'Tối thiểu 18' },
{ type: 'max', value: 100, message: 'Tối đa 100' },
// Special formats
{ type: 'email', message: 'Email không hợp lệ' },
{ type: 'url', message: 'URL không hợp lệ' },
{ type: 'regex', value: /^\d{9,12}$/, message: 'CCCD không đúng định dạng' },
// Custom validation
{
type: 'custom',
value: (value, formValues) => value !== formValues.password,
message: 'Mật khẩu không khớp'
}
]Custom Validator
import { ValidatorUtils } from '@fidt/dynamic-form'
ValidatorUtils.addCustomValidator('isAdult', (rule, value) => {
const age = parseInt(value)
return age >= 18
})
// Sử dụng
validation_rules: [
{ type: 'isAdult', message: 'Phải từ 18 tuổi trở lên' }
]🎯 FormModel API
Properties
interface FormItem {
name: string // Tên form
description?: string // Mô tả
id?: string // ID duy nhất
fields: FormField[] // Danh sách fields
columns?: number // Số cột grid
layout?: (string | null | string[])[][] // Layout grid
css_classes?: string // CSS classes tùy chỉnh
disabled?: boolean // Disable toàn bộ form
invalid?: boolean // Trạng thái invalid (readonly)
}Methods
// Validation
form.validate(): boolean // Validate toàn bộ form
form.validateField(name, value): boolean // Validate 1 field
// Get/Set values
form.getFormValue(): Record<string, any> // Lấy tất cả giá trị
form.setFormValue(values: Record<string, any>): void // Set nhiều giá trị
form.setFieldValue(name: string, value: any): void // Set 1 giá trị
// Errors
form.getFormErrors(): Record<string, string> // Lấy tất cả lỗi
form.getFormError(name: string): string | undefined // Lấy lỗi của 1 field
form.setFormError(name: string, error: string): void // Set lỗi cho 1 field
// Reset
form.reset(): void // Reset form về trạng thái ban đầu🎨 Events
<DynamicForm
:form="form"
@submit="onSubmit"
@value-change="onValueChange"
@loading="onLoading"
@submit-result="onSubmitResult"
/>Event handlers
function onSubmit(values: Record<string, unknown>) {
// Được gọi khi form submit và validation pass
console.log('Submit data:', values)
}
function onValueChange(values: Record<string, unknown>) {
// Được gọi mỗi khi có field thay đổi
console.log('Current form values:', values)
}
function onLoading(isLoading: boolean) {
// Trạng thái loading khi submit
console.log('Loading:', isLoading)
}
function onSubmitResult(result: any) {
// Kết quả sau khi submit (nếu có saulFunctionName)
console.log('Result:', result)
}🎛️ Field Configuration
FormField Interface
interface FormField {
name: string // Tên field (unique)
label?: string // Label hiển thị
type: string // Component type (VD: 'FInput', 'FButton')
role?: 'submit' | 'reset' | 'field' // Vai trò của field
required?: boolean // Bắt buộc
disabled?: boolean // Disable
required_text?: string // Thông báo lỗi khi required
props: any // Props truyền cho component
error?: string // Error message (readonly)
classes?: string // CSS classes
value?: any // Giá trị hiện tại
validation_rules?: ValidationRule[] // Quy tắc validation
}Ví dụ Field types
// Input
{
name: 'username',
type: 'FInput',
props: { placeholder: 'Enter username', type: 'text' }
}
// Button
{
name: 'submit',
type: 'FButton',
role: 'submit',
props: { label: 'Submit', order: 'primary', size: 'large' }
}
// Select (tùy component library)
{
name: 'country',
type: 'FSelect',
props: {
options: [
{ label: 'Vietnam', value: 'vn' },
{ label: 'USA', value: 'us' }
]
}
}🎨 Custom Styling
CSS Classes
.dymik-form {
// Form container
&.layout-mode {
// Grid layout mode
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: 1rem;
}
.field {
// Field wrapper
label {
.required {
color: red;
}
}
.error {
color: red;
font-size: 0.875rem;
}
}
.field-group {
// Nested fields container
.nested-fields {
display: flex;
gap: 0.5rem;
}
}
}Custom CSS Classes
const form = new FormModel({
name: 'my-form',
css_classes: 'custom-form theme-dark', // Form-level classes
fields: [
{
name: 'email',
classes: 'highlighted-field', // Field-level classes
// ...
}
]
})🔌 Tích hợp với UI Components
Dynamic Form hoàn toàn component-agnostic. Bạn có thể sử dụng với bất kỳ UI library nào:
// Với FIDT UI Components
{
type: 'FInput',
props: { placeholder: 'Enter text' }
}
// Với Element Plus
{
type: 'el-input',
props: { placeholder: 'Enter text' }
}
// Với Ant Design Vue
{
type: 'a-input',
props: { placeholder: 'Enter text' }
}
// Hoặc custom component của bạn
{
type: 'MyCustomInput',
props: { /* custom props */ }
}Lưu ý: Component phải hỗ trợ v-model và emit value-change event.
📚 Ví dụ hoàn chỉnh
Xem thêm các ví dụ trong thư mục examples/:
- ContactForm.vue - Form liên hệ đơn giản
- ThreeColumnForm.vue - Form đăng ký 3 cột với nested fields
🤝 Contributing
Contributions, issues và feature requests đều được chào đón!
📝 License
MIT © fidt
