form-esm
v1.0.0
Published
A dynamic form generator module with schema-driven forms, supporting Angular 18+ and modern frontend frameworks
Downloads
18
Maintainers
Readme
FormESM
一个功能强大的动态表单生成器模块,通过 JSON 元数据自动生成完整的响应式表单,支持 Angular 18+ 和现代前端框架。
特性
- 📝 Schema-Driven Forms - 通过 JSON 配置自动生成表单
- 🎯 多种字段类型 - text, email, password, number, textarea, select, checkbox, radio, date, file, custom
- 🏗️ 嵌套表单组 - 支持对象和数组类型的嵌套结构
- 🔗 条件显示 - 字段间的联动,根据条件显示/隐藏字段
- 🌐 异步验证 - 支持远程校验,防抖处理
- ⚠️ 实时错误提示 - 即时验证反馈,友好的错误消息
- 🎨 现代化样式 - 内置美观的默认样式,支持自定义
- 📱 响应式设计 - 自动适配不同屏幕尺寸
- 🔧 TypeScript 支持 - 完整的类型定义
- 🌍 跨框架 - 兼容 Angular 18+、React、Vue 等现代框架
安装
npm install form-esm快速开始
基础使用
import { createFormGenerator } from 'form-esm';
const schema = {
fields: [
{
key: 'username',
type: 'text',
label: '用户名',
placeholder: '请输入用户名',
required: true,
validations: [
{ type: 'required', message: '用户名不能为空' },
{ type: 'minLength', value: 3, message: '用户名至少3个字符' }
]
},
{
key: 'email',
type: 'email',
label: '邮箱',
placeholder: '请输入邮箱地址',
required: true,
validations: [
{ type: 'required', message: '邮箱不能为空' },
{ type: 'email', message: '请输入有效的邮箱地址' }
]
}
]
};
const form = createFormGenerator({
schema,
events: {
onValueChange: (value) => {
console.log('表单值变化:', value);
},
onSubmit: async (value) => {
console.log('提交表单:', value);
await submitToServer(value);
}
}
});
// 渲染表单
const container = document.getElementById('form-container');
const { FieldRenderer } = require('form-esm');
const renderer = new FieldRenderer(form);
form.schema.fields.forEach(field => {
const fieldContainer = document.createElement('div');
renderer.render(field, fieldContainer);
container.appendChild(fieldContainer);
});字段类型
文本字段 (Text)
{
key: 'username',
type: 'text',
label: '用户名',
placeholder: '请输入用户名',
required: true,
validations: [
{ type: 'minLength', value: 3, message: '至少3个字符' },
{ type: 'maxLength', value: 20, message: '最多20个字符' },
{ type: 'pattern', value: /^[a-zA-Z0-9_]+$/, message: '只能包含字母、数字和下划线' }
]
}数字字段 (Number)
{
key: 'age',
type: 'number',
label: '年龄',
min: 18,
max: 120,
step: 1,
required: true,
validations: [
{ type: 'min', value: 18, message: '年龄必须满18岁' },
{ type: 'max', value: 120, message: '年龄不能超过120岁' }
]
}文本域 (Textarea)
{
key: 'bio',
type: 'textarea',
label: '个人简介',
placeholder: '请输入个人简介',
rows: 4,
resize: 'vertical',
validations: [
{ type: 'maxLength', value: 500, message: '简介最多500个字符' }
]
}选择字段 (Select)
{
key: 'gender',
type: 'select',
label: '性别',
required: true,
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
{ label: '其他', value: 'other' }
]
}多选:
{
key: 'hobbies',
type: 'select',
label: '爱好',
multiple: true,
options: [
{ label: '阅读', value: 'reading' },
{ label: '运动', value: 'sports' },
{ label: '音乐', value: 'music' }
]
}分组选项:
{
key: 'category',
type: 'select',
label: '分类',
optionGroups: {
'前端': [
{ label: 'JavaScript', value: 'js' },
{ label: 'TypeScript', value: 'ts' }
],
'后端': [
{ label: 'Node.js', value: 'node' },
{ label: 'Python', value: 'python' }
]
}
}复选框 (Checkbox)
{
key: 'agree',
type: 'checkbox',
label: '我同意服务条款',
required: true,
trueValue: 'yes',
falseValue: 'no'
}单选框 (Radio)
{
key: 'gender',
type: 'radio',
label: '性别',
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' }
]
}日期字段 (Date)
{
key: 'birthDate',
type: 'date',
label: '出生日期',
min: '1900-01-01',
max: '2024-12-31',
required: true
}文件字段 (File)
{
key: 'avatar',
type: 'file',
label: '头像',
accept: 'image/*',
multiple: false,
maxSize: 5 * 1024 * 1024, // 5MB
allowedTypes: ['image/jpeg', 'image/png', 'image/gif']
}自定义字段 (Custom)
{
key: 'customField',
type: 'custom',
label: '自定义组件',
component: MyCustomComponent,
componentInputs: {
placeholder: '请输入...',
maxLength: 100
},
componentOutputs: {
onChange: (value) => console.log('值变化:', value)
}
}嵌套表单组
对象类型
{
key: 'personalInfo',
type: 'group',
label: '个人信息',
fields: [
{
key: 'firstName',
type: 'text',
label: '名',
required: true
},
{
key: 'lastName',
type: 'text',
label: '姓',
required: true
}
]
}数组类型
{
key: 'skills',
type: 'array',
label: '技能列表',
minItems: 1,
maxItems: 5,
defaultItems: 1,
addButtonText: '+ 添加技能',
removeButtonText: '删除',
itemConfig: {
key: 'skill',
type: 'text',
label: '技能名称',
placeholder: '例如:JavaScript',
required: true
}
}数组中的嵌套对象:
{
key: 'experience',
type: 'array',
label: '工作经历',
itemConfig: {
type: 'group',
fields: [
{
key: 'company',
type: 'text',
label: '公司名称',
required: true
},
{
key: 'position',
type: 'text',
label: '职位',
required: true
}
]
}
}条件显示
根据其他字段的值来显示或隐藏字段:
const schema = {
fields: [
{
key: 'hasExperience',
type: 'checkbox',
label: '有工作经验'
},
{
key: 'yearsOfExperience',
type: 'number',
label: '工作年限',
conditionalDisplay: {
when: 'hasExperience',
operator: 'eq',
value: true,
show: true
}
},
{
key: 'studentStatus',
type: 'select',
label: '学生状态',
options: [
{ label: '在读', value: 'studying' },
{ label: '毕业', value: 'graduated' }
],
conditionalDisplay: {
when: 'hasExperience',
operator: 'eq',
value: false,
show: true
}
}
]
};支持的操作符:
eq- 等于ne- 不等于gt- 大于lt- 小于gte- 大于等于lte- 小于等于in- 包含在数组中notIn- 不包含在数组中contains- 包含字符串notContains- 不包含字符串
异步验证
支持远程校验,自动防抖:
{
key: 'username',
type: 'text',
label: '用户名',
required: true,
asyncValidation: {
url: '/api/check-username',
method: 'POST',
headers: {
'Authorization': 'Bearer token'
},
debounce: 500,
transformRequest: (value) => ({
username: value,
timestamp: Date.now()
}),
transformResponse: (response) => {
if (response.available) {
return { valid: true };
} else {
return { valid: false, message: '用户名已存在' };
}
}
}
}验证规则
内置验证器
validations: [
{ type: 'required', message: '必填项' },
{ type: 'minLength', value: 3, message: '最小长度' },
{ type: 'maxLength', value: 20, message: '最大长度' },
{ type: 'min', value: 0, message: '最小值' },
{ type: 'max', value: 100, message: '最大值' },
{ type: 'pattern', value: /^[a-zA-Z]+$/, message: '格式不正确' },
{ type: 'email', message: '邮箱格式不正确' },
{ type: 'url', message: 'URL格式不正确' }
]自定义验证器
{
key: 'password',
type: 'password',
label: '密码',
validations: [
{
type: 'custom',
validator: (value) => {
return value.length >= 8 &&
/[A-Z]/.test(value) &&
/[0-9]/.test(value);
},
message: '密码必须包含大写字母和数字,至少8个字符'
}
]
}API 参考
FormGenerator
构造函数
const form = createFormGenerator(options);options:
schema(FormSchema) - 表单配置initialValue(Object) - 初始值events(FormEvents) - 事件处理器cssClass(string) - 容器 CSS 类style(Object) - 容器样式debug(boolean) - 调试模式
方法
| 方法 | 说明 | 返回值 |
|------|------|--------|
| setValue(key, value) | 设置字段值 | void |
| getValue(key) | 获取字段值 | any |
| setValues(values) | 设置多个字段值 | void |
| getValues() | 获取所有表单值 | Object |
| validate() | 验证整个表单 | Promise<ValidationResult> |
| validateField(key) | 验证单个字段 | Promise<ValidationResult> |
| reset() | 重置表单到初始值 | void |
| clear() | 清空表单 | void |
| addField(config) | 添加字段 | void |
| removeField(key) | 删除字段 | void |
| showField(key) | 显示字段 | void |
| hideField(key) | 隐藏字段 | void |
| enableField(key) | 启用字段 | void |
| disableField(key) | 禁用字段 | void |
| getErrors() | 获取所有错误 | Object |
| getFieldErrors(key) | 获取字段错误 | string[] |
| isDirty() | 表单是否被修改 | boolean |
| isTouched() | 表单是否被触摸 | boolean |
| isValid() | 表单是否有效 | boolean |
| dispose() | 销毁表单 | void |
事件
events: {
onValueChange: (value) => void,
onFieldChange: (key, value) => void,
onFieldFocus: (key) => void,
onFieldBlur: (key) => void,
onSubmit: (value) => void | Promise<void>,
onReset: () => void,
onError: (errors) => void
}框架集成
Angular 18+
import { Component, ElementRef, ViewChild } from '@angular/core';
import { createFormGenerator } from 'form-esm';
@Component({
selector: 'app-dynamic-form',
template: `<div #formContainer></div>`
})
export class DynamicFormComponent {
@ViewChild('formContainer') formContainer!: ElementRef;
private form: any;
ngAfterViewInit() {
this.form = createFormGenerator({
schema: { fields: [...] },
events: { onValueChange: (value) => console.log(value) }
});
this.renderForm();
}
private renderForm() {
const { FieldRenderer } = require('form-esm');
const renderer = new FieldRenderer(this.form);
// 渲染表单...
}
}React
import { useEffect, useRef } from 'react';
import { createFormGenerator } from 'form-esm';
function DynamicForm() {
const formContainerRef = useRef(null);
const [form, setForm] = useState(null);
useEffect(() => {
const generator = createFormGenerator({
schema: { fields: [...] },
events: { onValueChange: (value) => console.log(value) }
});
setForm(generator);
renderForm(generator, formContainerRef.current);
return () => generator.dispose();
}, []);
return <div ref={formContainerRef}></div>;
}Vue
<template>
<div ref="formContainer"></div>
</template>
<script>
import { createFormGenerator } from 'form-esm';
export default {
mounted() {
this.form = createFormGenerator({
schema: { fields: [...] },
events: { onValueChange: (value) => console.log(value) }
});
this.renderForm();
},
methods: {
renderForm() {
const { FieldRenderer } = require('form-esm');
const renderer = new FieldRenderer(this.form);
// 渲染表单...
}
}
}
</script>样式定制
使用默认样式
<link rel="stylesheet" href="node_modules/form-esm/src/styles/default.css">自定义样式
:root {
--form-primary-color: #667eea;
--form-error-color: #ef4444;
--form-border-color: #d1d5db;
--form-radius: 6px;
}
.form-input {
border: 2px solid var(--form-primary-color);
border-radius: var(--form-radius);
}
.form-error {
color: var(--form-error-color);
font-weight: 600;
}浏览器支持
- Chrome 61+
- Firefox 60+
- Safari 11+
- Edge 16+
许可证
MIT License
相关链接
- Angular Forms - Angular 响应式表单
- React Hook Form - React 表单库
- VeeValidate - Vue 表单验证
