fx-schema-form-react
v3.1.13
Published
自动生成表单组件
Downloads
102
Maintainers
Readme
react-schema-form
爬虫前端核心组件。
DEMO
react-schema-form-react V3
通过json-schema,ui-schema,自动生成表单组件。前后端可以复用一份JsonSchema来验证字段,错误消息前后端统一,这个可以有。
Note: 组件之间的功能组合使用hoc来实现。
目录
安装
npm
npm install --save fx-schema-form-react
依赖项
- react
- redux
- react-redux
- immutable
- recompose
- ajv
- reselect
- redux-act
- resolve-pathname
- redux-immutable
Note: 请根据自己的项目情况选择使用。
性能
这里。更改一下数据,触发错误信息;以下是Schema和图片:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 10 },
"password": { "type": "string" }
}
}
}
默认使用
导入fx-schema-form-react的依赖
import schemaFormReact from "fx-schema-form-react";
const { reducerFactory, schemaFormDec, hocFactory, defaultTheme } = schemaFormReact;
实例化ajv
const curAjv: ajv.Ajv = new ajv({
allErrors: true,
jsonPointers: true,
useDefaults: true,
$data: true,
errorDataPath: "property",
removeAdditional: true,
});
创建store,初始化schemaform的reducer
这里的reducer的key是可以自定义的。从工厂类中获取schemaForm这个类;schemaForm是默认定义的reducer,可以自定义reducer来覆盖;
let store = createStore<any>(combineReducers({
"schemaForm": reducerFactory.get("schemaForm").reducer as any
}), Immutable.fromJS({}));
将schemaform的actions添加到store
这里的actions注册到store就可以直接使用,不需要在使用dispatch来触发action。
let actions = reducerFactory.get("schemaForm").actions;
for (const key in actions) {
if (actions.hasOwnProperty(key)) {
const element = actions[key];
element.assignTo(store);
}
}
加入template和widget
defaultTheme.tempFactory.add("default", NoneTemp as any);
defaultTheme.tempFactory.add("card", AntdCardTemp as any);
defaultTheme.tempFactory.add("formitem", AntdFormItemTemp as any);
defaultTheme.widgetFactory.add("checkbox", AntdCheckboxWidget as any);
defaultTheme.widgetFactory.add("default", AntdInputWidget as any);
defaultTheme.widgetFactory.add("number", AntdInputNumberWidget as any);
创建表单组件
/**
* SchemaForm的Hoc
* 加入了
* data 当前表单的数据,实时更新。
* root 当前表单的meta根节点。
* isValid 当前表单验证是否正确。
* isValidating 当前表单是否正在验证。
* errors 当前表单的所有错误信息。
*/
@(schemaFormDec({
ajv: curAjv,
schemaId: "design",
reducerKey: "schemaForm",
formKey: "designForm",
initData:{}
}) as any)
class TestForm extends React.PureComponent<any> {
/**
* @param RootComponent 使用Form来创建根元素
* @param schemaId schema的id是design
* @param uiSchemas 需要渲染的表单元素
* @param uiSchema 父亲uiSchema
* @param parentKeys 数据节点的根Keys,一般就等于crateForm中的key
* @param globalOptions 全局的配置
* @param ajv ajv的实例
*/
public render() {
const { isValidating = false, isValid = false, validateAll, parentKeys, resetForm, schemaId } = this.props;
if (!this.props.root) {
return null;
}
return <>
<FormComponent
validateAll={validateAll}
isValid={isValid}
resetForm={resetForm}
RootComponent={NoneComponent}
schemaId={schemaId}
uiSchemas={[{
key: "children",
field: "design"
}]}
parentKeys={parentKeys}
globalOptions={globalOptionsOfDesign}
ajv={curAjv} >
</FormComponent>
</>;
}
}
实例化SchemaForm组件
ReactDOM.render(
<Provider store={store}>
<div>
<TestForm />
</div>
</Provider>,
document.getElementById("root"),
() => {
console.log("form ok!");
});
表单定制化
全局配置参数
默认分为3块内容:
- field: 所有的field的参数配置在此;
- temp: 所有的模板的参数配置在此;
- hoc: 所有的hoc的参数配置在此;
const gloabelOptions = Immutable.fromJS({
field: {
// 这里定义了field默认的参数
// 所有的field参数都会默认合并default的配置
default: {
temps: ["formitem"],
widgetHocs: [schemaFormReact.hocFactory.get("data")({
data: true
})]
},
// 数组字段类型配置
array: {
// 定义了包裹的模板
temps: ["card"],
// 添加自定义的hoc,来增强功能
// 为array字段添加sort排序功能使用[react-sortable-hoc]组件地址:
https://github.com/clauderic/react-sortable-hoc
// 与【fieldHocs】的区别在于
// 包裹的位置不同,formHocs是包裹在field组件内部的根元素上;fieldHocs是包裹在field组件上
formHocs: [(Component: any) => {
class SortableComponentWrapper extends React.PureComponent<any> {
private _onSortEnd: any;
constructor(props: any) {
super(props);
this._onSortEnd = this.onSortEnd.bind(this);
}
private onSortEnd({ oldIndex, newIndex }: { oldIndex: number; newIndex: number; }) {
const { uiSchema, parentKeys } = this.props;
if (oldIndex === newIndex) {
return;
}
this.props.moveItem(parentKeys, uiSchema.keys, oldIndex, newIndex);
}
public render() {
return <Component useWindowAsScrollContainer={true}
pressDelay={300} onSortEnd={this._onSortEnd} {...this.props} />;
}
}
return SortableComponentWrapper;
},
// react-sortable-hoc 提供的hoc
SortableContainer],
// 数组的子元素添加 SortableElement hoc,使得可以排序
formItemHocs: [SortableElement, shouldUpdate(() => false)],
// 这个字段包裹在field组件外面,用于获取数组的元素个数
fieldHocs: [schemaFormReact.hocFactory.get("data")({
data: true,
dataLength: true
})]
},
normal: {},
object: {
temps: ["card"]
}
},
temp: {
card: {
// 包裹外temp组件外部的hoc,用于获取数据
tempHocs: [schemaFormReact.hocFactory.get("data")({
meta: true,
metaKeys: ["errorText", "isValid", "collapsing"]
}), immutableRenderDecorator],
// 组件的默认参数,根据不同的组件会有所不同
options:{}
},
formitem: {
// 包裹外temp组件外部的hoc,用于获取数据
tempHocs: [schemaFormReact.hocFactory.get("data")({
meta: true,
metaKeys: ["isLoading", "errorText", "isValid", "dirty"]
}), immutableRenderDecorator],
// 组件的默认参数
options: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
}
}
},
// hoc的默认参数
hoc: {
// dataHoc的参数
data: {
rootReducerKey: ["schemaForm"]
},
// 数组hoc的参数
array: {
ArrayComponent: ArrayComponent,
ArrayItemComponent: ArrayItemComponent
}
}
})
UiSchema配置
uiSchema的参数配置:
- options?: Immutable.Map<string, any>; 定义参数,与全局的配置参数格式一致;会覆盖全局的配置参数,用于单个FormSchemaItem的配置。
- children?: Array<UiSchema | string>; 一般用于object和array的对象;用于渲染下级显示的key。
- theme?: string; 主题样式配置(default: default)。
- field?: string; 定义字段。默认使用JsonSchema的type来确定field,也可以使用这个来指定字段。
- widget?: string; 定义显示组件。每种数据类型都可以使用不同的组件来渲染。
- temps?: string[]; 定义包裹的模板数组。
- isRequired?: boolean; 是否是required,自动添加。
- readonly?: boolean; 是否是只读的。
- hocs?:Array<string | ComponentEnhancer>; 用于包裹整个ShemaFormItem的hoc数组。(default:["theme", "field", "validate", "array", "temp"])
模板
模板是一个用来包裹Widget的组件(例如:Div、Row、Col、Card、FormItem等,其中Card和FormItem需要显示错误信息或者当前状态,所以需要加入hoc来获取数据,这个配置在全局配置中的temp一节)
HOCS
名称 | 说明 | 依赖 | 加入属性 | |- |- |- | - | ThemeHoc | 解决主题样式| null | currentTheme | FieldHoc | 取得FieldComponent和WidgetComponent | ThemeHoc, UtilsHoc | FieldComponent,WidgetComponent | ValidateHoc | 验证以及数据操作相关 | null | updateItemData,updateItemMeta,validate | ArrayHoc | 数组的相关操作 | UtilsHoc | addItem,removeItem,moveItem,initArrayComponent,ArrayComponent,ArrayItemComponent | TempHoc | 模板的归并 | ThemeHoc, UtilsHoc, [ArrayHoc] | null | DataHoc | 用于从reducer中获取数据 | UtilsHoc | [formItemData,formItemMeta,formItemNode] | MakeHoc | 用于FormItem的包裹Hoc合并 | UtilsHoc | null | UtilsHoc | 工具类Hoc | null | getOptions,getTitle,getPathKeys,normalizeDataPath,getRequiredKeys | MergeHoc | jsonschema和uischema合并 | null | null |
ThemeHoc
配置参数: null
返回属性:
- currentTheme: NsFactory 获取当前的主题样式工厂类;
FieldHoc
配置参数: null
返回属性:
- FieldComponent: new() => React.PureComponent; 根据schema的配置获取对应的FieldComponent
- WidgetComponent: new() => React.PureComponent; 根据schema的配置来获取对应的WidgetComponent
ValidateHoc
配置参数: null
返回属性:
- updateItemData: (props, data, meta?) => void; 提交数据,触发更改数据action
- updateItemMeta: (props, data, meta?, noChange?) => void; 提交meta数据,触发更改meta的action
- removeItemData: (props, meta?) => void; 删除当前数据和meta数据
- validate: (props, data, meta) => any; 验证data的合法性
ArrayHoc
配置参数: null
返回属性:
- addItem: (props,data) => Promise; 数组中添加一项到末尾
- removeItem: (parentKeys,keys,index) => void; 数组删除一个元素
- moveItem: (parentKeys,keys,index) => void; 数组中2个元素交换位置
- initArrayComponent: (props,index) => JSX.Element; 根据当前的schema返回ArrayComponent
- ArrayComponent: React.PureComponent; 数组的操作组件
- ArrayItemComponent: React.PureComponent; 数组中子元素的操作组件
TempHoc
配置参数:
- tempField: string; 配置中的字段名称
- templates: string[]; 需要使用的temps
返回属性: null
DataHoc
配置参数:
- data: boolean; 是否需要数据
- dataLength: boolean; 是否需要数据的长度
- meta: boolean; 是否需要meta数据
- metaKeys: string[]; meta数据中的字段过滤
- treeNode: boolean; 是否需要treeNode
返回属性:
- formItemData: any; 当前组件的数据
- formItemMeta: Immutable.Map; 当前组件的meta数据
- formItemNode: TreeMap; 当前组件对应的tree
MakeHoc
配置参数:
- hocs: Array<string|ComponentEnhancer>; 需要compose的hoc数组
返回属性: null
UtilsHoc
配置参数: null
返回属性:
- getOptions: (props,category,field,...extraSettings) => Object; 获取当前元素的配置参数
- getTitle: (props,...extraSettings) => string; 获取当前元素的标题
- getPathKeys: (keys,path) => string[]; 获取当前元素keys的相对keys
- normalizeDataPath: (schemaId,dataPath) => string[]; 格式化keys
- getRequiredKeys: (props,include,exclude) => string[]; 获取当前props中所需的prop
- getDefaultData: (ajv,schema,defaultData,merge) => Promise; 获取schema的默认数据
MergeHoc
配置参数: null
返回属性:
- mergeSchemaList: FxUiSchema[]; 合并之后的数组
字段
字段决定了如何渲染一个数据结构。举个栗子:
{
"type":"object",
"title": "表示一个点的坐标",
"properties":{
"x":{
"type":"number"
},
"y":{
"type":"number"
}
}
}
这里的结构是一个ObjectField对象,所以默认会嵌套一层SchemaForm,然后渲染出x和y的文本框。 如果我想x和y在一行上显示;那这里就要用到自定义的Field,参照自定义Field一节;这里我们自定义一个PointField,在里面直接放入2个文本框,当文本框更改的时候,我们更新相对应的值就可以了,so easy。
默认字段:
NormalField
普通数据类型字段,直接渲染WidgetComponent。
配置项:
- widgetHocs: Array<string|ComponentEnhancer>; 为WidgetComponent包装hoc
ArrayField
根据数组元素的个数,嵌套渲染N个ShemaForm。
配置项:
- formHocs: Array<string|ComponentEnhancer>; 数组根元素包装hocs
- formItemHocs: Array<string|ComponentEnhancer>; 数组中每个元素包装hocs
ObjectField
直接嵌套一层SchemaForm。
配置项:
- formHocs: Array<string|ComponentEnhancer>; 为WidgetComponent包装hoc
高级配置
为了增强功能,很多时候需要自定义一些hoc;
自定义hoc
hoc是schema-form的核心功能;比如接口请求,条件判断,数据处理等等。举个例子:
JsonSchema中有format字段配置,我们想根据format来更改渲染组件。
/**
* format装饰器
* 根据指定的format来配置相对应的组件
* 例如:当format=date的时候,使用datetime组件
* @param hocFactory hoc的工厂方法
* @param Component 需要包装的组件
*/
export const hoc = (hocFactory: BaseFactory<any>) => {
const name = "format";
return () => {
return (Component: any): RC<Props, any> => {
class ComponentHoc extends React.PureComponent<Props, any> {
/**
* 渲染组件
*/
public render(): JSX.Element | null {
const { uiSchema, getOptions } = this.props,
{ format, field } = uiSchema,
hocOptions = getOptions(this.props, schemaFormTypes.hoc, name);
// 根据当前jsonschema中配置的format
// 查看配置中是否有定义,如果有则合并到uiSchema中
if (format && hocOptions[format] && !field) {
Object.assign(uiSchema, hocOptions[format]);
}
return <Component {...this.props} />;
}
}
return ComponentHoc as any;
};
};
};
自定义字段
自定义字段是为了解决一些特殊的数据格式。比如我们有以下数据结构:
{
"type": "object",
"$id": "design",
"required": ["title"],
"properties": {
"title": {
"type": "string",
"title": "名称"
},
"children": {
"type": "array",
"title": "图表组件",
"items": {
"type": "object",
"required": ["data"],
"properties": {
"type": {
"type": "string"
},
"sourceId": {
"type": "number"
},
"data": {
"type": "object",
"default": {},
"design": true
},
"children": {
"$ref": "design#/properties/children"
}
}
}
}
}
}
这里是一个树形结构,如果我们想渲染成tree。默认的ArrayField显然不能满足需求。具体请查看源码
这里使用了3个Form组件,只是使用的field不同。
自定义模板
为了满足不同的样式,不同的需求,需要各种各样的模板组件。
自定义组件
为了满足不同的样式组件,这里需要自己建立很多的模组件,比如(input,select,mension...);
验证
本地验证
本地验证,直接使用json-schema的各种关键字来定义。
当然也可以自定义一些验证,关于自定义验证请查看这里
远程验证
ajv默认支持远程验证,文档在这里