npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fx-schema-form-react

v3.1.13

Published

自动生成表单组件

Downloads

102

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

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<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默认支持远程验证,文档在这里

License

MIT