上一篇文章对高扩展性、插件式前端开发框架—— sifo 进行了初步介绍,说明了其高扩展性的主要特点。高扩展性的其中一个方面是模型插件的高扩展性,下面将详细介绍一下官方最近发布的表单内核模型插件,及在 React 和 Vue 下,以 antd 和 antd-vue 为UI层的表单组件。
官方也提供了这两个场景的 codeSandbox 示例:antd 和 antdv ,可以直接访问并修改代码体验。
这篇文章需要开发者事先了解 sifo 的基本使用方法,建议先看一下上一篇介绍文章,或查看快速上手示例:react、vue
antd 为UI层的效果
表单内核模型插件
- 有什么功能
sifo-mplg-form-core(后面简称SifoFormCore)是一个不包含任何UI的表单处理内核,对表单字段进行统一的管理,并提供了一些表单操作 api(setValues/getValues/validate/addValidator等)。
sifo 是与UI框架和组件库完全解耦的,使用模型插件可以实现各式各样的功能,表单内核模型插件就是一个例子。
SifoFormCore 在 React 和 Vue 下都能使用,这样能保持使用方式一致。接入组件库作为UI层时只需要按组件库的事件约定处理即可,开发者也可以自己封装UI层,参照官方的antd、antdv方式。
SifoFormCore 可以使用 sifo 所有的扩展能力与特性,这是该表单与一般的组件库表单相比显著的特点。利用插件化和扩展能力,能很好地处理不同客户个性的、差异性的需求。
- 如何使用
import SifoFormCore from '@schema-plugin-flow/sifo-mplg-form-core'; // sifo modelPlugin { modelPlugin: { plugin: SifoFormCore, argsProvider: (mId, info) => { return { // 字段的key,无返回值认为不是字段 fieldKey: (attr) => attr.props && attr.props.name, fieldChange: { // 表单字段change使用的handler changeHandler: (context, e) => { const { event, mApi } = context; let val = e; if (typeof e === "string" || typeof e === "number") { val = e; } else if (typeof e === "object") { val = e.target ? e.target.value : e; } mApi.setAttributes(event.key, { value: val }, true); }, eventName: "change", // 组件库的值更新事件名 }, }; }, } }
- schema 格式
默认将 value, validators, rules, validateDisabled, validateInfo 等放在 attributes上,且 schema 节点 id 应与 fieldKey(此处指字段标识对应的值)一致。默认使用attributes.name作为字段标识。
{ "id": "fieldKey01", "component": "Input", "attributes": { "name": "fieldKey01", "label": "字段名", "rules": [ { "required": true, "message": "你需要填写fieldKey01", "trigger": [ "pressEnter", "change" ] } ] } }
- 校验规则描述
- 内置了必填、整数、最大最小值和长度校验;
- 规则描述的message省略时将使用内置文案;
- trigger省略时将使用类实例参数 fieldChange 的 eventName;
- 调用validate、validateAll 时不区分 trigger;
{ "rules": [ { "required": true, "message": "请填写信息", "trigger": [ "pressEnter", "change" ] }, { "type": "integer" }, { "max": 99.9 }, { "maxLength": 9 } ] }
- 校验函数
使用 mApi.addValidator 可以为指定字段追加校验函数,格式如下:
const validatorItem = { validator: (value, callback, opts:{ id, mApi, fieldKey }) => { // not passed callback({ passed: false, status: 'error', message: 'invalidate message', }); // passed callback(); }, trigger: ['eventName'], // 在何时触发,默认是类实例参数 fieldChange 的 eventName }
- UI层封装
此模型插件只有表单内核,不包含任何UI部分,UI可按各自的情形封装。
-
字段标识: 在字段节点时,在schema节点层和attributes层都将包含属性__isField__: true;
-
默认将 value, validators, rules, validateDisabled, validateInfo 等放在 attributes 上;使用sifo-vue时,运行时这些属性在props上,定义时可直接放在schema的attributes上(sifo-vue内做了分类);
-
可在模型插件 onComponentsWrap 生命周期中包装组件以进行 FormItem 的样式相关封装;
-
可扩展、覆盖 mApi 方法来适配相应情形;
Ant-design 表单
表单主要是进行样式及校验信息展示的处理,开发者也可以通过formItemWrapper覆盖默认的处理方法。使用示例:
import SifoFormCore from '@schema-plugin-flow/sifo-mplg-form-core'; import AntdFormModelPlugin from "@schema-plugin-flow/sifo-mplg-form-antd"; // 样式: @import "~@schema-plugin-flow/sifo-mplg-form-antd/index.less"; // sifo plugins [{ modelPlugin: { plugin: SifoFormCore, argsProvider: (mId, info) => { return { // 字段的key,无返回值认为不是字段 fieldKey: (attr) => attr.name, fieldChange: { // 表单字段change使用的handler changeHandler: (context, e) => { const { event, mApi } = context; // 可根据不同的组件进行值获取 let val = e; if (typeof e === "string" || typeof e === "number") { val = e; } else if (typeof e === "object") { val = e.target ? e.target.value : e; } mApi.setAttributes(event.key, { value: val }, true); }, eventName: "onChange", // antd的字段组件事件名是 onChange }, }; }, } }, { modelPlugin: AntdFormModelPlugin, // 要在SifoFormCore之后注册 }, { pagePlugin, componentPlugin } ]
Ant-design-vue 表单
表单主要是进行样式及校验信息展示的处理,开发者也可以通过formItemWrapper覆盖默认的处理方法。使用示例:
import SifoFormCore from '@schema-plugin-flow/sifo-mplg-form-core'; import AntdVueFormModelPlugin from "@schema-plugin-flow/sifo-mplg-form-antdv"; // 样式: @import "~@schema-plugin-flow/sifo-mplg-form-antdv/index.less"; // sifo plugins [{ modelPlugin: { plugin: SifoFormCore, argsProvider: (mId, info) => { return { // 字段的key,无返回值认为不是字段;vue的一般属性都在props上,此处用 props.name 作为字段标识 fieldKey: (attr) => attr.props && attr.props.name, fieldChange: { // 表单字段change使用的handler changeHandler: (context, e) => { const { event, mApi } = context; // 可根据不同的组件进行值获取 let val = e; if (typeof e === "string" || typeof e === "number") { val = e; } else if (typeof e === "object") { val = e.target ? e.target.value : e; } mApi.setAttributes(event.key, { value: val }, true); }, eventName: "change", // antd-vue的字段组件事件名是 change }, }; }, } }, { modelPlugin: AntdVueFormModelPlugin, // 要在SifoFormCore之后注册 }, { pagePlugin, componentPlugin } ]
字段属性与逻辑处理
-
字段FormItem属性
schema中没有显式的FormItem层,其实是在UI层模型插件中提供的能力,如果需要还可以自定义其它能力。目前官方的UI层支持这些样式属性:
- “class”: “test-subject”,
- “itemClassName”: “subject-form-item”,
- “labelCol”: {
“span”: 8 }, - “wrapperCol”: {
“span”: 8,
“offset”: 0 }
-
组件插件
字段可以注册自己的插件来控制值更新事件及联动:
subject: { onComponentInitial: (params) => { const { event, mApi } = params; mApi.addEventListener(event.key, 'change', (ctx, e) => { // 监听值更新事件并更改原始值 mApi.setAttributes(event.key, { value: e.target.value + '修改值' }); }); // watch 属性变化并控制其它节点 mApi.watch(event.key, (ctx, state, oldState) => { // 关联校验 const val = mApi.getValue(event.key); mApi.disableValidate('typeDesc', val !== 'customized'); }); } },
- 表单提交
一般使用如下方式:
submitBtn: { onComponentInitial: (params) => { const { event, mApi } = params; mApi.addEventListener(event.key, "click", (context) => { mApi.validateAll().then((e) => { if (e.passed) { const values = mApi.getValues(); console.log("表单数据:", values); // post form data } else { console.error("表单校验未通过", e); } }); }); }, }
最后
官方后面计划提供更多组件库为UI层的表单组件,如Fusion/antd-mobile等,也会不断完善表单内置能力。热切欢迎大家提出宝贵的意见与建议、分享更多实用的模型插件和使用场景等。