fx-schema-form-react

3.1.13 • Public • Published

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" }
        }
    }
}
 

200个表单,使用同一个redux的数据

默认使用

导入fx-schema-form-react的依赖

import schemaFormReact from "fx-schema-form-react";
 
const { reducerFactory, schemaFormDec, hocFactory, defaultTheme } = schemaFormReact;

实例化ajv

详细的配置请查看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

默认没有templatewidget,需要加入一些组件。

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的文本框。 默认的ObjectField 如果我想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<Propsany> => {
            class ComponentHoc extends React.PureComponent<Propsany> {
                /**
                 * 渲染组件
                 */
                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默认支持远程验证,文档在这里

License

MIT

install

npm i fx-schema-form-react

Downloadsweekly downloads

139

version

3.1.13

license

MIT

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar
Report a vulnerability