ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# **自定义控件前端2.0开发概述** 本文档仅适用于协同版本为V8.0&CAP4.5及以上版本的CAP4表单. V8.0&CAP4.5前版本的自定义控件前端没有提供统一规范,在实际开发过程基本上是自行发挥,"借鉴"已开发的工程,开发门槛较高,开发者不知如何下手,同时也暴露了一些比较低级的错误。针对这些问题,提出了自定义控件前端2.0规范,定义自定义控件前端的基本生命周期、事件、校验接口等。 自定义控件前端2.0确定了以下内容: - 注册自定义控件统一接口 - 确定了自定义控件基类,明确了控件的生命周期、事件、API - 规范基于csdk开放API来操作自定义控件、表单及进行数据通信 - 提供了Promise支持(csdk已经注入polyfill),建议异步操作都使用Promise ## **1. 启用自定义控件2.0** 自定义控件2.0在每个控件上增加了一个`version`属性,`version`设为'2.0'即代表启用自定义控件2.0版本。 ![](https://img.kancloud.cn/b1/d2/b1d2624508445dd0f426f393b06c3589_422x192.png) ## **2. 自定义控件2.0生命周期** 自定义控件生命周期大致分为下载、安装、渲染、更新、销毁等过程。 - 下载: 表单根据接口返回的控件定义信息`customFieldInfo`下载自定义控件的主js资源及国际化文件 - 安装: 资源下载完毕后调用控件定义的安装脚本(install)处理控件的初始化、业务逻辑等 - 渲染: 当表单渲染到该自定义控件时,自定义控件的渲染方法被调用 - 更新: 当控件数据发生变化时,自定义控件的更新方法被调用 - 销毁: 当控件被表单主动销毁时,自定义控件的销毁方法被调用 详见下图 ![](https://img.kancloud.cn/e1/76/e176c7b2c175215e1d33b13babb58ea4_1270x933.png "自定义控件生命周期图解") ## **3. 注册自定义控件** 用`csdk.component.register(uuid, factory)`申明自定义控件 - uuid: 自定义控件的唯一标识,建议使用`单词+uuid`实现,保证全局唯一及可读性 - factory: 自定义控件的具体实现,返回结构参照下图 ```javascript csdk.component.register('myWidget-12345678', function () { return { .... } }); ``` 自定义控件的基本代码结构如下 ![](https://img.kancloud.cn/d6/95/d69569d7addf0ff8bf152a280923dbd4_1080x1126.png "自定义控件代码结构") ## **4. 生命周期钩子及API详细说明** 下面对自定义控件的生命周期方法详述。 #### **- type** 申明自定义控件实现的类型,可选值raw和vue, 当前版本仅支持raw。 - raw: 原生js实现 - vue: vue组件实现 #### **- plugin.install(context)** 自定义控件资源下载完毕后调用plugin.install(contxt)执行安装脚本,一些常见的业务场景: - 加载控件使用到的其他资源,如js、css、图片等 - 按次、按量计费的控件,超量超次控件不可使用,在install期间校验 - 从服务器读取控件的相关业务配置 参数/返回值说明: @param {Object} context {package: '自定义控件控件包地址'},可用于加载包内的资源 @return Promise | undefined | Any - install方法返回Promise,则表单会等待异步操作完成后才开始渲染自定义控件。 - install方法返回undefined, 表示操作完成。 - install方法返回其他值,表示在安装中遇到错误,无法继续渲染(比如付费服务的调用次数用完)。 示例: ```javascript // 没有异步,操作成功 plugin: { install: function (context) { // 异步操作返回Promise,示例检查接口调用次数是否用完 return new Promise(function (resolve) { ajax('/xyz/validateApi', { success: function(res) { if (res.count <= 0) return; // 次数用完 resolve(); } }) }) // 安装脚本出错 try { a.b(); // 模拟报错 } catch (ex) { return ex || 'unknow error'; } // 返回undefined,表示没有异步操作且成功 return; } } ``` #### **- plugin.requiredAssert(fieldData)** 控件必填校验逻辑,由开发者根据业务需要自行实现,默认校验字段的value是否有值 参数/返回值说明: @param {Object} fieldData: <FieldData> 自定义控件对象 @return Boolean #### **- plugin.fieldLengthAssert(fieldData)** 控件长度校验,由开发者根据业务需要自行实现,默认校验通过 参数/返回值说明: @param {Object} fieldData: <FieldData> 自定义控件对象 @return Boolean #### **- plugin.validAssert(fieldData)** 控件自定义场景校验,由开发者根据业务需要自行实现,默认校验通过 参数/返回值说明: @param {Object} fieldData: <FieldData> 自定义控件对象 @return Boolean #### **- implement.init(context, container)** 控件初始化渲染,由开发者根据自身业务实现控件的渲染,事件绑定等,`该方法必须实现` 参数/返回值说明: @param {Object} context 初始化渲染上下文参数 @param {HTMLElement} container 控件渲染的容器元素 @return Promise | Any 如果返回Promise, 则控件渲染完成事件会等待promise完成后触发 context参数说明 ```javascript option: { uuid: '', // 由表单生成唯一标识,用于识别控件实例,进行数据通信 scope: '', // 所在表单视图作用域,用于区分包含视图引用(视图嵌套)场景 fieldId: '', // 控件的数据域id recordId: '', // 明细行数据id tableName: '', // 明细表名 container: HTMLElement // 容器元素 } ``` 示例: ```javascript init: function (context, container) { console.log('init ' + this.$data.display) var ctx = this; var el = document.createElement('div'); el.style.cssText = 'padding: 10px 5px; color: white; background: gray'; el.innerHTML = this.$t('cap.form.loading'); // 国际化 setTimeout(function () { el.innerHTML = 'test ' + ctx.$data.value; }, 1000); container.appendChild(el); this.$el = el; // 绑定事件 this.$on(this.$el, 'tap', function () { // 更新数据 ctx.$set({ value: 'event changed ' + Date.now() }); }); return new Promise(function (resolve) { // 模拟异步渲染 setTimeout(function () { this.$el.innerHTML = 'async rendering'; resolve(); }, 1000); }); } ``` #### **- implement.update()** 当控件数据发生变化时,会触发该方法,由开发者实现渲染更新 示例: ```javascript update: function () { this.$el.innerHTML = 'updated at ' + Date.now() + '\n,new value is: ' + this.$data.value; } ``` #### **- implement.destroy()** 在控件被销毁前调用,由开发实现需要清理的事件、数据等 示例: ```javascript destroy: function () { console.log('destroy ' + this.$data.display) this.$el = null; } ``` #### **- implement.beforeSave()** 在表单保存时调用,由开发者实现当前控件需要在保存前的业务处理 参数说明: @return Promise | Any 如果返回Promise,则表单会等待异步操作完成继续提交 <small>注1: 由于交互问题,移动端轻表单模式只支持调用主表中的自定义控件该方法</small> <small>注2: 由于数据分页,只支持调用在页面渲染好的(即可见)控件的该方法</small> <small>注3: 除非特殊需要,尽可能减少这种场景设计</small> 示例: ```javascript beforeSave: function () { console.log('beforeSave ' + this.$data.display); return new Promise((resolve) => { // do sth cost 2 seconds setTimeout(resolve, 2000); }); } ``` #### **- implement.afterSave()** 表单提交后调用该方法,由于保存后页面会关闭或者刷新,只建议实现同步调用 示例: ```javascript afterSave: function () { console.log('Saved'); } ``` #### **- $t(i18nKey, replacer)** 获取国际化词条 ```javascript fI18nData['cap.form.mywidget.chineseDate'] = '{0}年{1}月'; this.$t('cap.form.mywidget.chineseDate', ['2020', '4']); ``` #### **- $set(data)** 更新当前控件数据 示例: ```javascript data = { value: 'value', // 控件的值 display: 'showValue', // 控件的显示值 auth: 'auth', // 控件的权限 atts: Object, // 待定,尚未完全支持 placeholder: 'placeHolder' // 输入框占位符 }; this.$set({ value: 'newValue', display: 'newShowValue', auth: 'browse' }) ``` #### **- $on(el, type, listener) 和 $on(type, listener)** 控件事件绑定,支持绑定dom事件和控件自定义事件,使用方式如下: - $on(el, type, listener) : 绑定dom事件 - $on(type, listener) : 绑定控件自定义事件 _调用this.$on绑定的事件,在控件销毁时,会被自动销毁,开发者无须关注_ #### **- $emit(type, data)** 控件触发自定义事件 ## **5. 自定义控件事件** EVENT_RENDERED: 自定义控件渲染完,需开发者保证 EVENT_UPDATED: 自定义控件更新完成 EVENT_DESTROYED: 自定义控件销毁 示例: ```javascript csdk.event.on(csdk.component.EVENT_RENDERED, function(evt) { console.log('component rendered', evt); }); ``` ## **6. 与表单、其他控件交互通信** 与自定义控件1.0版本相比,2.0版本只在初始化提供必要参数,1.0版本中提供的一些其他数据不再传入。如需要获取表单数据、获取其他控件数据、更新表单、更新其他控件(包括自定义控件自身)、监听数据变化等请使用表单提供的csdk开发套件。[csdk](http://example.com/ "csdk").开发套件提供了一套PC和移动一致的API及事件,可实现大部分代码重用。 csdk相关文档见: [csdk API接口](https://www.kancloud.cn/cap4_development/cap-doc/1424196 "csdk API接口") [csdk 事件接口](https://www.kancloud.cn/cap4_development/cap-doc/1424194 "csdk 事件接口") [csdk 钩子](https://www.kancloud.cn/cap4_development/cap-doc/1424195 "csdk 钩子") *End*