文本介绍如何进行字段数据的展示和修改。
系统组件展示和修改数据
目前我们的组件还没有使用平台的数据,在此之前,我们先以系统组件为例,看下系统组件如何展示和修改数据。
首先我们创建一个实体来表示业务数据。姓名字段是我们要展示和修改的数据。
然后搭建相关的页面进行展示和修改。
这里用到三个系统组件:
Data 组件通过上下文数据源加载数据(Data 绑定上下文数据源,若没有上下文对象传递,会自动创建该对象。)。
文本组件展示字段。
输入框组件修改字段。
最后我们看下实际运行的效果。
在输入框内输入内容,上面的文本组件也会跟着改变。
Data 组件加载实体对象,文本组件展示实体对象字段,输入框组件展示和修改数据实体字段。
另外基于平台的数据驱动能力,当字段发生改变时,对应的组件会重新渲染,展示新的数据。
添加组件展示和修改数据能力
接下来,我们在自定义组件上添加数据展示和修改能力。
在 src/config.ts 中添加属性配置。
import { API_VERSION, Category } from './api'; export default { apiVersion: API_VERSION, version: '1.0', isContainer: false, title: '', componentName: 'MobiPcComponentABC', category: Category.COMMON, icon: 'https://img.alicdn.com/imgextra/i1/O1CN01oufFHB266LbYRkVr3_!!6000000007612-55-tps-20-20.svg', configure: [ { title: '数据相关的Prop设置', type: 'group', display: 'block', items: [ { name: 'aAttr', title: '选择展示的字段', setter: { type: 'data-bind-field', props: { supportFieldType: ['TEXT', 'CHAR'], } }, }, ], }, ], };
字段对应的属性设置器是data-bind-field。
在组件中添加 Props 参数和处理代码。
import React from 'react'; import { AttrMeta, PropsWithPageContext } from './api'; import config from './config'; import './index.scss'; interface Props { aAttr: AttrMeta; } const MobiPcComponentABC = (props: PropsWithPageContext<Props>) => { const attrValue = props.pageContext.getAttrValue<string>(props.aAttr); return ( <div className="MobiPcComponentABC"> <div>{`Hello MobiPcComponentABC ~`}</div> <div>{`字段名称 : ${props.aAttr?.name}, 字段类型 : ${ props.aAttr?.type } 字段数值 : ${attrValue.get()} `}</div> <input value={attrValue.get()} onChange={(e) => { attrValue.set(e.target.value); }} /> </div> ); }; MobiPcComponentABC.config = config; export default MobiPcComponentABC;
这里有几点需要说明:
data-bind-field 属性设置器配置的 Props 参数类型是AttrMeta,表示平台用户具体设置的实体字段信息,根据 data-bind-field 属性设置器配置的参数不同,还有可能是联合类型,具体请查看data-bind-field。
获取、更新字段数据是调用getAttrValue API,参数就是AttrMeta。
getAttrValue API 挂载在平台向组件 Props 注入的 API 接口 PageContext 上。
PropsWithPageContext 是方便组件开发者定义 Props 的辅助类型,参见 PropsWithPageContext。
组件设计时加载效果如下,我们配置上对应字段:
组件运行时加载效果如下:可以看到组件正确的展示了字段数据,而且输入框组件修改字段,自定义组件也会更新,自定义组件修改字段,文本、输入框组件都会更新。
设计时和运行时区分
在上面修改自定义组件的过程中,我们可以看到,设计时(应用搭建时)并不会有数据存在。
为了提高组件使用方的体验,我们可以区分设计时、运行时(应用真正运行时)展示不同的效果。
比如文本组件和输入框组件,设计时展示的是 {姓名},运行时展示的是姓名字段的值。
平台提供了一些 API 来支持实现这些效果:
isPreview 判断是否是运行时。
formatForDesignTips 统一包装要展示的字符串,添加{}。
组件是否区分设计时和运行时,由组件开发者自己决定,平台不会有强制要求。
实现思路总结
和事件响应类似,数据展示和修改也是基于属性设置,设置的 Props 参数本身并不是数据,而是实体字段的定义,具体的数据需要通过 API 获取,修改也是通过 API 修改。
总结来说步骤如下:
添加一个属性,使用data-bind-field,使用户可以配置要操作的实体字段。
添加对应的 Props 参数,类型为AttrMeta。
在组件渲染时,通过getAttrValue API 获取数据的获取和更新接口,渲染数据。
可以考虑区分设计时和运行时的渲染内容,提高用户体验。
在组件触发数据变化的回调方法中,调用更新接口,更新数据。
与 React 组件的区别
一般处理数据的 React 组件,都会有两个 Props 参数,一个用于传递数据,一个用于响应数据变化。如下:
import React from 'react';
interface Props {
value: string;
onChange: (value: string) => void;
}
const ComponentA = (props: Props) => {
const { value, onChange } = props;
return (
<div>
<input value={value} onChange={(e) => onChange(e.target.value)} />
</div>
);
};
value:要展示的数据
onChange:数据变化时的回调方法,新数据以参数的形式返回。
主要区别如下:
平台组件的 Props 参数只需要一个,含义是实体字段模型,并不是数据本身。相应数据的获取,就需要调用 API 来获取
数据更新时,不需要通过 Props 回调参数反馈给应用,而是通过调用 API 更新数据。
数据驱动
在上面的例子中,当实体字段发生改变后,使用字段的组件会自动重新渲染,这是平台提供的一种数据驱动能力。
在开发自定义组件时,要充分的利用数据驱动能力,有一些限制需要注意:
字段数据必须在render方法内读取。反之,如果render方法执行过程中没有读取字段数据,当此字段更新时,组件是不会因为数据驱动触发render的。
字段数据的读取必须是在最外层的组件内读取, 举例如下:
import React from 'react'; import { AttrMeta, PageContext, PropsWithPageContext } from './api'; interface PropsB { aAttr: AttrMeta; pageContext: PageContext; } const ComponentB = (props: PropsB) => { return <div>{props.pageContext.getAttrValue<string>(props.aAttr).get()}</div>; }; interface PropsA { aAttr: AttrMeta; } const ComponentA = (props: PropsWithPageContext<PropsA>) => { return <ComponentB aAttr={props.aAttr} pageContext={props.pageContext} />; }; export default ComponentA;
假设 ComponentA 是直接暴露给平台的组件,而 ComponentB 是 ComponentA 内部定义的其它组件,字段类型是CHAR。
这里把 aAttr 和 pageContext 传递给 ComponentB,由 ComponentB 来读取字段数据。
在这种情况下,当字段发生变化时,并不会触发 ComponentA 和 ComponentB 的重新渲染。
如果要支持数据驱动,需要做如下改动:
import React from 'react'; import { AttrMeta, PropsWithPageContext } from './api'; interface PropsB { value: string; } const ComponentB = (props: PropsB) => { return <div>{props.value}</div>; }; interface PropsA { aAttr: AttrMeta; } const ComponentA = (props: PropsWithPageContext<PropsA>) => { return <ComponentB value={props.pageContext.getAttrValue<string>(props.aAttr).get()} />; }; export default ComponentA;
把字段数据的读取放到 ComponentA 内,直接传递数据给 ComponentB 使用。这样字段发生变化时,ComponentA 会重新渲染,从而使 ComponentB 也重新渲染。
静态数据和动态数据
有一些场景,组件需要的数据并不一定是实体对象的字段。
比如文本组件,展示的文本内容可以是平台用户在搭建时设置的静态字符串,也可以是实体对象的字段,甚至是模板字符串。
平台为这种场景提供了以下支持:
data-bind-field通过修改配置的参数,可以实现由用户选择数据类型。对应的 Props 参数也会变成联合类型。比如可以是静态字符串,也可以是实体字段,Props 参数的类型就会变成 AttrMeta | string 这种类型。
getAttrValue API 兼容静态数据和模板字符串这两种场景。如果用户设置的是静态字符串或者模板字符串,相应的更新接口就会是空实现(因为静态字符串和模板字符串不能修改)。
data-bind-field 属性设置器是支持配置允许设置的字段类型的,所以对于限制了类型的字段,可以类似 getAttrValue<string> 或者 getAttrValue<boolean> 这样获取确定的结果。
字段类型定义
在创建实体模型时,实体字段有对应的字段类型(参考新增字段的说明),在组件代码中,对应关系如下
字段类型 | 对应代码定义 | 备注 |
INTEGER LONG DECIMAL | number | JS的基础类型 |
BOOLEAN | boolean | JS的基础类型 |
CHAR TEXT | string | JS的基础类型 |
DATETIME | Date | JS的标准内置对象 |
ENUMERATION | EnumValue | 平台API定义的对象EnumValue |
FILE IMAGE AUDIO VIDEO DOCUMENT | FileValue | 平台API定义的对象FileValue |
接下来
掌握了组件如何展示和修改数据后,让我们看下6. 开发容器组件,学习让组件变成容器组件。