fx-schema-form-core

schemaForm的核心组件。用于解析JsonSchema,为SchemaForm提供支持。

Usage no npm install needed!

<script type="module">
  import fxSchemaFormCore from 'https://cdn.skypack.dev/fx-schema-form-core';
</script>

README

fx-schema-form-core

schemaForm的核心组件。用于解析JsonSchema,为SchemaForm提供支持。

依赖项

  • ajv
  • jsonschema

重要的概念

jsonschema;用于描述json的结构。 uischema;用于描述界面的表现形式,是jsonschema的增强属性。

todolist

  1. 去掉ajv的依赖

Schema DEMO

 let schema = {
    type: "object",
    $id: "dnd-oneof",
    title: "oneof测试schema",
    default: {},
    required: ["value"],
    properties: {
        type: {
            type: "number",
            enum: [1, 2, 3, 4],
            title: "类型选择",
            description: "1:数字,2:字符串,3:bool,4:object"
        },
        value: {
            oneOf: [{
                $id: "dnd-oneof-number",
                type: "number",
                title: "这是一个数字类型"
            }, {
                $id: "dnd-oneof-string",
                type: "string",
                title: "这是一个字符串类型"
            }, {
                $id: "dnd-oneof-boolean",
                type: "boolean",
                title: "这是一个bool类型"
            }, {
                $id: "dnd-oneof-object",
                type: "object",
                title: "这是一个object类型",
                default: {},
                required: ["a", "b"],
                properties: {
                    a: {
                        type: "string",
                        default: "nick"
                    },
                    b: {
                        type: "boolean",
                        default: true
                    }
                }
            }]
        }
    }
};

API

schemaKeysFactory

存储了所有key与schemaKey的对应关系

example: 'dnd-oneof-object/a': 'dnd-oneof-object#/properties/a',

schemaFieldFactory

存储了schemaKey对应的schema

example:

{
    'dnd-oneof-object#/properties/a': {
        type: 'string',
        default: 'nick',
        keys: ['value','a'],
        schemaPath: 'dnd-oneof-object#/properties/a'
    }
}

schemaKeyWordFactory

所有的keyword处理方式。目前有ref和oneOf;

  • ref: 处理schema中的$ref关键字
  • oneOf: 处理schema中的oneOf关键字
  • anyOf: 处理schema中的anyOf关键字
  • definitions: 处理schema中的definitions关键字

schemaTypeFactory

schema类型的处理方式

  • array schema中数组类型
  • object schema中的对象类型
  • undefined schema中的其他简单类型(string,number,integer,any,boolean)
schemaTypeFactory.add("array", array);
schemaTypeFactory.add("string", none);
schemaTypeFactory.add("undefined", none);
schemaTypeFactory.add("number", none);
schemaTypeFactory.add("null", none);
schemaTypeFactory.add("any", none);
schemaTypeFactory.add("integer", none);
schemaTypeFactory.add("boolean", none);
schemaTypeFactory.add("object", object);

ResolveLib

解析schema中的所有字段,存储到【schemaFieldFactory】中

/**
 * 构造函数
 * @param ajv      Ajv的一个实例
 * @param schema   jsonschema
 * @param $id      schema的$id字段,用于找到schema
 */
constructor(private ajv: Ajv, schema: JSONSchema6, public readonly $id = "")

MergeLib

解析uiSchama,与对应的schema合并

/**
 * 构造函数
 * @param ajv        Ajv的一个实例
 * @param schema     schemaPath
 * @param parent     父亲schema
 * @param uiSchemas  uiSchemas
 */
constructor(ajv: Ajv, private schemaPath: string, public parent: UiSchema | null = null, private uiSchemas: Array<UiSchema | string> = ["*"]) {

基础使用

定义Schema

const schema: JSONSchema6 = {
    type: "object",
    $id: "design",
    required: ["name", "dsModelIds"],
    properties: {
        name: {
            type: "string",
            title: "面板名称"
        },
        description: {
            type: "string",
            title: "面板详情"
        },
        appType: {
            type: "string",
            title: "应用类型"
        },
        dsModelIds: {
            type: "array",
            items: {
                type: "number"
            }
        },
        dsModelData: {
            type: "object",
            properties: {
                data: {
                    type: "object"
                },
                ids: {
                    type: "object"
                }
            }
        },
        infoOptions: {
            type: "array",
            items: {
                type: "object",
                properties: {
                    label: {
                        type: "string"
                    },
                    data: {
                        type: "object"
                    },
                    infoOptions: {
                        $ref: "design#/properties/infoOptions"
                    }
                }
            }
        }
    }
};

解析上一步中定义的schema,curAjv是一个ajv的实例。

let someResolve = new ResolveLib(curAjv, schema);

最后与定义的uiSchema做合并。

/**
 * 参数说明
 * @param $1 ajv的一个实例
 * @param $2 jsonschema的当前路径
 * @param $3 父亲schema
 * @param $4 uiSchema
 * @return Array类型
 */
let merge = new MergeLib(curAjv, "design", null, ["infoOptions/-"]);

返回值:

[{
    "type": "object",
    "properties": {
        "label": {
            "type": "string",
            "keys": ["infoOptions", "-", "label"]
        },
        "data": {
            "type": "object",
            "keys": ["infoOptions", "-", "data"]
        },
        "infoOptions": {
            "$ref": "design#/properties/infoOptions",
            "keys": ["infoOptions", "-", "infoOptions"]
        }
    },
    "keys": ["infoOptions", "-"],
    "schemaPath": "design#/properties/infoOptions/items",
    "key": "design/infoOptions/-"
}]

如何使用ajv的功能动态获取jsonschema

{
    "type": "object",
    "$id": "echart",
    "title": "echarts参数设置",
    "default": {},
    "properties": {
        "title": {
            "$ref": "echart-title#"
        },
        "toolbox": {
            "$ref": "echart-toolbox#"
        },
        "tooltip": {
            "$ref": "echart-tooltip#"
        },
        "legend": {
            "$ref": "echart-legend#"
        },
        "series": {
            "$ref": "echart-series#"
        },
        "xAxis": {
            "$ref": "echart-axis#"
        },
        "yAxis": {
            "$ref": "echart-axis#"
        }
    }
}

在做echart的配置界面的时候,由于参数众多,而且很多参数都重复,不得不把echart的jsonschema拆分成若干个json文件来复用。上面的json文件使用$ref来连接其他的schema,使得schema的依赖过于繁琐。如果我们要使用这个schema,就必须先解析其依赖的schema,这样就有问题了。我们能不能不关系依赖呢?

解决方案:

  • 这个方法用于加载我们需要的schema文件。
    /**
     * 动态加载schema方法
     * 可以在组件中去使用
    */
    const initSchema = (schemaId: string) => {
        // getSchema是一个接口方法,用来获取json的数据
        return getSchema.get(null, {
            params: {
                id: schemaId + ".json"
            }
        }).then((schema: JSONSchema6) => {
            // 这里使用compileAsync来获取schema的验证信息
            // 这个方法可以解析依赖关系,然后使用ajv中的loadSchema来加载依赖的jsonschema
            curAjv.compileAsync(schema).then(() => {
                // 解析获取的schema,这里已经解析完了所有的依赖
                let resolve = new ResolveLib(curAjv, schema);
            });
        }).catch(() => {
            console.error("fetch schema [" + uri + "] error!");
        });
    }
  • 配置ajv中的loadSchema方法,用于加载$ref中使用的schema文件。
// 初始化ajv实例
export const curAjv: ajv.Ajv = ajvErrors(new ajv({
    allErrors: true,
    jsonPointers: true,
    useDefaults: true,
    format: "full",
    $data: true,
    errorDataPath: "property",
    removeAdditional: true,
    // 关键参数
    loadSchema: initSchema
}));
  • react示例
    import { ResolveLib, schemaKeysFactory } from "fx-schema-form-core";

    class ComponentHoc extends React.PureComponent<any, any> {

        /**
         * 加载完毕后,update当前组件
         */
        private initSchema(schemaId: string){
            initSchema(schemaId).then(this.forceUpdate);
        }

        /**
        * 如果当前的schema不存在,则远程拉取
        */
        public render(): JSX.Element | null {
            const { schemaId, ...extraProps } = this.props;

            if (!schemaKeysFactory.has(schemaId)) {
                this.initSchema(schemaId);
                return null;
            }

            return <span>schema已经解析好了</span>;
        }
    }

命令

  • npm test 测试命令
  • npm run build 打包命令

License

MIT