Note: As the project is not yet fully completed, there is still a risk of code refactoring. Please use with caution. The following only provides simple examples of usage. Detailed documentation will be provided upon official release.
DEHub attempts to be a message-driven library for JavaScript-based clients. It unifies the handling of events and data in all clients by using messages for subscription and processing. This allows all components and business processes in a page to handle business logic in a subscribable manner, and enables access to all registered components through context.
Features:
- Supports component registration
- Allows accessing all registered components in callback functions for event subscriptions
- Supports data subscription
- Supports singleton maintenance for data, ensuring that the same data request is only completed on one instance (this is important)
- Supports data set loading
- Supports serial loading of data sets
test('Register a component', async () => {
// Component tag, used for referencing the component
const tags = { id: 'cmp1' };
// Register the component and use SessionDB to store component information
let comp1 = new DEComp<any>(tags);
await RegComponent(comp1);
// Update the component state
await comp1.update({ a: 1, b: 'abc' });
// Check the updates
expect(comp1.state.a).toBe(2); // X by default, this test will not pass
expect(comp1.state.b).toBe('abc');
});
To make the 'test' in the component test above pass, you can use 'when' to subscribe to the specified event, and update the value of 'a' to 2 after the 'update' is called.
when({ id: 'cmp1', event: EventNames.StateChanged, stage: EventStage.PostOperation },
(context: EventContext) => {
const comp1 = context.sender as DEComp;
// You can also retrieve the registered component using the following method:
// const comp1 = context.compoents[ObjPath({id:'user1'})];
comp1.update({ a: 2 });
});
const IdTag = (entity: string, id: string): ObjTag => {
const tag: ObjTag = { entity, id };
return tag;
};
test('Modify and submit data', async () => {
// Data object tag, with user id as 'user1'
const user1Tag = IdTag('user.demo@org0', 'user1');
// Subscribe to the data
const user1Data = DEData(user1Tag);
// Wait for data to finish loading
await sleep(50);
expect(user1Data.get('name')).toBe('Jim');
// Change the data properties, note that the updated values will be stored in 'dirty'
user1Data.set("age", 18);
user1Data.set("name", 'Jim2');
user1Data.set("att1", 'a');
user1Data.set("att2", new Date());
// Wait for the update to complete
await sleep(20);
// Check the updated results, 'get' by default includes the values in 'dirty'
expect(user1Data.get('age')).toBe(18);
expect(user1Data.get('name')).toBe('Jim2');
expect(user1Data.get('att1')).toBe('a');
// 'dirty' stores the unsaved data
expect(user1Data.dirty('age')).toBe(18);
expect(user1Data.dirty('att1')).toBe('a');
// 'original' still stores the original data; note that 'get' will return 'Jim2' at this point
expect(user1Data.original('name')).toBe('Jim');
// Submit the data
await user1Data.submit();
// After submission, the data in 'dirty' will be cleared
expect(user1Data.dirty('age')).toBeUndefined();
// The latest data is now in 'original'
expect(user1Data.original('name')).toBe('Jim2');
}, 2000);
/**
* When loading 'user1', simulate providing the data for 'user1';
*/
when({ entity: 'user.demo@org0', id: 'user1', status: DataStatus.Loading },
(context: EventContext) => {
context.output.data = { name: 'Jim' };
});
/**
* When submitting 'user1', simulate returning the submission result to 'context.output.data';
*/
when({
entity: 'user.demo@org0',
id: 'user1',
event: EventNames.Submit,
status: DataStatus.Uploading,
stage: EventStage.PreOperation
},
(context: EventContext) => {
const sender: DEData = context.sender as DEData;
if (!sender) return;
context.output.data = sender.data;
});
注意:因项目未列发完成,代码仍有重构风险,谨慎使用,以下仅提供简单的调用示例,待正式发布时提供详细的文档。
DEHub尝试作为基于js的客户端的消息驱动库,将所有客户端的事件与数据变统一由消息的形式进行订阅处理,以便页面中的所有组件及业务流程能够订阅的方式处理业务逻辑,并能够通过上下文访问所有已注册组件。
特性:
- 支持组件注册
- 支持在事件订阅的回调中访问所有注册的组件
- 支持订阅数据
- 支持数据单例维护,所有相同的数据请求只会在一个实例上完成,这个很重要;
- 支持数据集加载
- 支持数据集串行加载
test('注册一个组件', async () => {
//组件标签,可用于引用组件
const tags = { id: 'cmp1' };
//注册组件,并使用SessionDB存储组件信息
let comp1 = new DEComp<any>(tags);
await RegComponent(comp1);
//更新组件state
await comp1.update({ a: 1, b: 'abc' });
//检查更新检查
expect(comp1.state.a).toBe(2); // X 默认,该处无法通过测试
expect(comp1.state.b).toBe('abc');
});
想让上面的组件测试中的test测试通过,可以用when来订阅指定事件,并在更新update后,更新a值为2。
when({ id: 'cmp1', event: EventNames.StateChanged, stage: EventStage.PostOperation },
(context: EventContext) => {
const comp1 = context.sender as DEComp;
// 也可以通过以下方式获取注册的组件;
//const comp1 = context.compoents[ObjPath({id:'user1'})];
comp1.update({ a: 2 });
});
const IdTag = (entity: string, id: string): ObjTag => {
const tag: ObjTag = { entity, id };
return tag;
};
test('数据修改并提交', async () => {
//数据对象标签,用户id为user1
const user1Tag = IdTag('user.demo@org0', 'user1');
//订阅数据
const user1Data = DEData(user1Tag);
//等待数据加载完成
await sleep(50);
expect(user1Data.get('name')).toBe('Jim');
//变更数据属性值,注意:该处更新的值都会进入dirty中;
user1Data.set("age", 18);
user1Data.set("name", 'Jim2');
user1Data.set("att1", 'a');
user1Data.set("att2", new Date());
//等待更新完成
await sleep(20);
//判断更新结果,默认get取值包含了脏数据的值 ;
expect(user1Data.get('age')).toBe(18);
expect(user1Data.get('name')).toBe('Jim2');
expect(user1Data.get('att1')).toBe('a');
//在脏数据dirty保存未提交的数据
expect(user1Data.dirty('age')).toBe(18);
expect(user1Data.dirty('att1')).toBe('a');
//原数据仍保存在original,注意,此时直接用get获取name是Jim2
expect(user1Data.original('name')).toBe('Jim');
//提交数据
await user1Data.submit();
//提交完成后,dirty的数据会被清空
expect(user1Data.dirty('age')).toBeUndefined();
//此时最新数据更新到original中
expect(user1Data.original('name')).toBe('Jim2');
}, 2000);
/**
* 加载user1时,模拟提供user1的数据;
*/
when({ entity: 'user.demo@org0', id: 'user1', status: DataStatus.Loading },
(context: EventContext) => {
context.output.data = { name: 'Jim' };
});
/**
*
* 提交user1时,模拟返回提交成功到context.output.data中;
*/
when({
entity: 'user.demo@org0',
id: 'user1',
event: EventNames.Submit,
status: DataStatus.Uploading,
stage: EventStage.PreOperation
},
(context: EventContext) => {
const sender: DEData = context.sender as DEData;
if (!sender) return;
context.output.data = sender.data;
});