对 Schema 节点进行增、删、改操作,并且提供了事件触发机制,用于将数据同步到服务端。
1interface Options {
2 current: Schema;
3 api?: APIClient;
4 onSuccess?: any;
5 refresh?: () => void;
6 t?: any;
7}
8
9interface InsertAdjacentOptions {
10 wrap?: (s: ISchema) => ISchema;
11 removeParentsIfNoChildren?: boolean;
12 breakRemoveOn?: ISchema | BreakFn;
13 onSuccess?: any;
14}
15
16class Designable {
17 constructor(options: Options ) { }
18 loadAPIClientEvents(): void;
19 on(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', listener: any): void
20 emit(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', ...args: any[]): Promise<any>
21 refresh(): void
22 recursiveRemoveIfNoChildren(schema?: Schema, options?: RecursiveRemoveOptions): Schema;
23 remove(schema?: Schema, options?: RemoveOptions): Promise<any>
24 removeWithoutEmit(schema?: Schema, options?: RemoveOptions): Schema
25 insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
26 insertBeforeBeginOrAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
27 insertBeforeBegin(schema: ISchema, options?: InsertAdjacentOptions): void
28 insertAfterBegin(schema: ISchema, options?: InsertAdjacentOptions): void
29 insertBeforeEnd(schema: ISchema, options?: InsertAdjacentOptions): Promise<any>
30 insertAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
31}
参数讲解
current
:需要操作的 Schema 节点api
:用于发起后端请求的 APIClient 实例onSuccess
:后端接口请求成功后的回调refresh
:用于更新节点后,刷新页面t
:useTranslation()
的返回值示例
1const schema = new Schema({
2 type: 'void',
3 name: 'hello',
4 'x-component': 'div',
5})
6
7const dn = new Designable({ current: schema });
1const schema = new Schema({
2 type: 'void',
3 name: 'a',
4 properties: {
5 b: {
6 type: 'void',
7 properties: {
8 c: {
9 type: 'void',
10 }
11 }
12 }
13 }
14});
15
16const b = schema.b;
17
18const dn = createDesignable({
19 current: b,
20})
21
22console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
移除当前节点
1dn.remove();
2console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26dn.remove();
27
28export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
1{
2 type: 'void',
3 name: 'a',
4 properties: {
5- b: {
6- type: 'void',
7- properties: {
8- c: {
9- type: 'void',
10- }
11- }
12- }
13 }
14}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
1dn.insertBeforeBegin({
2 type: 'void',
3 name: 'd',
4});
5console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26dn.insertBeforeBegin({
27 type: 'void',
28 name: 'd',
29});
30
31export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
1{
2 type: 'void',
3 name: 'a',
4 properties: {
5+ d: {
6+ type: 'void',
7+ },
8 b: {
9 type: 'void',
10 properties: {
11 c: {
12 type: 'void',
13 }
14 }
15 }
16 }
17}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
1dn.insertAfterBegin({
2 type: 'void',
3 name: 'd',
4});
5console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26dn.insertAfterBegin({
27 type: 'void',
28 name: 'd',
29});
30
31export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
1{
2 type: 'void',
3 name: 'a',
4 properties: {
5 b: {
6 type: 'void',
7 properties: {
8+ d: {
9+ type: 'void',
10+ },
11 c: {
12 type: 'void',
13 }
14 }
15 }
16 }
17}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
1dn.insertBeforeEnd({
2 type: 'void',
3 name: 'd',
4});
5console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26dn.insertBeforeEnd({
27 type: 'void',
28 name: 'd',
29});
30
31export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
1{
2 type: 'void',
3 name: 'a',
4 properties: {
5 b: {
6 type: 'void',
7 properties: {
8 c: {
9 type: 'void',
10 },
11+ d: {
12+ type: 'void',
13+ },
14 }
15 }
16 }
17}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
1dn.insertAfterEnd({
2 type: 'void',
3 name: 'd',
4});
5console.log(schema.toJSON());
1import React from 'react';
2import { Schema } from '@tachybase/schema';
3import { createDesignable } from '@tachybase/client';
4
5const schema = new Schema({
6 type: 'void',
7 name: 'a',
8 properties: {
9 b: {
10 type: 'void',
11 properties: {
12 c: {
13 type: 'void',
14 },
15 },
16 },
17 },
18});
19
20const b = schema.properties['b'];
21
22const dn = createDesignable({
23 current: b,
24});
25
26dn.insertAfterEnd({
27 type: 'void',
28 name: 'd',
29});
30
31export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
1{
2 type: 'void',
3 name: 'a',
4 properties: {
5 b: {
6 type: 'void',
7 properties: {
8 c: {
9 type: 'void',
10 }
11 }
12 },
13+ d: {
14+ type: 'void',
15+ },
16 }
17}
根据第一个参数决定插入的位置,是前面四个方法的封装。
1class Designable {
2 insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
3}
on
:添加事件监听的基础方法loadAPIClientEvents
:调用 on
方法添加对 insertAdjacent
、patch
、batchPatch
、remove
的事件的监听,主要功能是将变更的 Schema 更新到服务端emit
:是根据事件名称,调用之前注册过的方法,具体是由前面讲过的 插入操作和删除操作 触发而 loadAPIClientEvents()
并非在初始化时调用,需要手动调用,换而言之,如果不调用 dn.loadAPIClientEvents()
,则不会将更新发送到服务端,主要是简化在单测或者 DEMO 环境对服务端的 Mock。
对 new Designable()
的简单封装。
1function createDesignable(options: CreateDesignableProps) {
2 return new Designable(options);
3}
1const dn = createDesignable({ current: schema });
用户获取当前节点 Schema JSON 对象,更多信息请参考 formily useFieldSchema()。
1import { Schema } from '@tachybase/schema';
2
3const useFieldSchema: () => Schema;
1/**
2 * defaultShowCode: true
3 */
4import React from 'react';
5import { useFieldSchema } from '@formily/react';
6import { Application, Plugin, SchemaComponent } from '@tachybase/client';
7const Demo = ({ children }) => {
8 const fieldSchema = useFieldSchema();
9 return <div style={{ border: '1px solid red' }}>
10 <pre>{ JSON.stringify(fieldSchema, null, 2)}</pre>
11 <div style={{ paddingLeft: 20 }}>{children}</div>
12 </div>
13}
14const schema = {
15 type: 'void',
16 name: 'hello',
17 'x-component': 'Demo', // 这里是 Demo 组件
18 'properties': {
19 'world': {
20 'type': 'void',
21 'x-component': 'Demo', // 这里也是 Demo 组件
22 },
23 }
24}
25
26const Root = () => {
27 return <SchemaComponent components={{ Demo }} schema={schema} />
28}
29
30const app = new Application({
31 providers: [Root]
32})
33
34export default app.getRootComponent();
获取当前节点 Schema 实例,更多信息请参考 formily useField()
1import { GeneralField } from '@formily/core';
2const useField: <T = GeneralField>() => T;
1const Demo = () => {
2 const field = useField();
3 console.log('field', field);
4 return <div></div>
5}
6
7const schema = {
8 type: 'void',
9 name: 'hello',
10 'x-component': 'Demo', // 这里是 Demo 组件
11 'properties': {
12 'world': {
13 'type': 'void',
14 'x-component': 'Demo', // 这里也是 Demo 组件
15 },
16 }
17}
18
19const Root = () => {
20 return <SchemaComponent components={{ Hello }} schema={schema} />
21}
对当前 Schema 节点的修改操作。
1interface InsertAdjacentOptions {
2 wrap?: (s: ISchema) => ISchema;
3 removeParentsIfNoChildren?: boolean;
4 breakRemoveOn?: ISchema | BreakFn;
5 onSuccess?: any;
6}
7
8type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
9
10function useDesignable(): {
11 dn: Designable;
12 designable: boolean;
13 reset: () => void;
14 refresh: () => void;
15 setDesignable: (value: boolean) => void;
16 findComponent(component: any): any;
17 patch: (key: ISchema | string, value?: any) => void
18 on(name: "error" | "insertAdjacent" | "remove" | "patch" | "batchPatch", listener: any): void
19 remove(schema?: any, options?: RemoveOptions): void
20 insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void
21 insertBeforeBegin(schema: ISchema): void;
22 insertAfterBegin(schema: ISchema): void;
23 insertBeforeEnd(schema: ISchema): void;
24 insertAfterEnd(schema: ISchema): void;
25}
详细解释
Designable
的实例null
dn.remove
方法dn.on
方法dn.insertAdjacent
方法
dn.insertBeforeBegin
方法dn.insertAfterBegin
方法dn.insertBeforeEnd
方法dn.insertAfterEnd
方法示例
插入节点。
1import React from 'react';
2import {
3 SchemaComponentProvider,
4 SchemaComponent,
5 useDesignable,
6} from '@tachybase/client';
7import { observer, Schema, useFieldSchema } from '@formily/react';
8import { Button, Space } from 'antd';
9import { uid } from '@formily/shared';
10
11const Hello = observer(
12 (props) => {
13 const { insertAdjacent } = useDesignable();
14 const fieldSchema = useFieldSchema();
15 return (
16 <div>
17 <h1>{fieldSchema.name}</h1>
18 <Space>
19 <Button
20 onClick={() => {
21 insertAdjacent('beforeBegin', {
22 'x-component': 'Hello',
23 });
24 }}
25 >
26 before begin
27 </Button>
28 <Button
29 onClick={() => {
30 insertAdjacent('afterBegin', {
31 'x-component': 'Hello',
32 });
33 }}
34 >
35 after begin
36 </Button>
37 <Button
38 onClick={() => {
39 insertAdjacent('beforeEnd', {
40 'x-component': 'Hello',
41 });
42 }}
43 >
44 before end
45 </Button>
46 <Button
47 onClick={() => {
48 insertAdjacent('afterEnd', {
49 'x-component': 'Hello',
50 });
51 }}
52 >
53 after end
54 </Button>
55 </Space>
56 <div style={{ margin: 50 }}>{props.children}</div>
57 </div>
58 );
59 },
60 { displayName: 'Hello' },
61);
62
63const Page = observer(
64 (props) => {
65 return <div>{props.children}</div>;
66 },
67 { displayName: 'Page' },
68);
69
70export default () => {
71 return (
72 <SchemaComponentProvider components={{ Page, Hello }}>
73 <SchemaComponent
74 schema={{
75 type: 'void',
76 name: 'page',
77 'x-component': 'Page',
78 properties: {
79 hello1: {
80 type: 'void',
81 'x-component': 'Hello',
82 },
83 },
84 }}
85 />
86 </SchemaComponentProvider>
87 );
88};
部分更新。
1import React from 'react';
2import {
3 SchemaComponentProvider,
4 SchemaComponent,
5 FormItem,
6 useDesignable,
7} from '@tachybase/client';
8import { observer, Schema, useField, useFieldSchema, uid } from '@tachybase/schema';
9import { Button } from 'antd';
10
11const Hello = observer(
12 (props) => {
13 const fieldSchema = useFieldSchema();
14 const field = useField();
15 const { dn } = useDesignable();
16 return (
17 <div>
18 <h1>{field.title}</h1>
19 { JSON.stringify(props) }
20 <br/>
21 { JSON.stringify(field.componentProps) }
22 <br/>
23 { JSON.stringify(field.decoratorProps) }
24 <br/>
25 { JSON.stringify(fieldSchema.toJSON()) }
26 <br/>
27 <Button onClick={() => {
28 dn.shallowMerge({
29 title: uid(),
30 'x-component-props': {a: uid(), },
31 'x-decorator-props': {b: uid(), },
32 });
33 }}>shallowMerge</Button>
34 <Button onClick={() => {
35 dn.deepMerge({
36 title: uid(),
37 'x-component-props': {c: uid() },
38 'x-decorator-props': {d: uid(), },
39 });
40 }}>deepMerge</Button>
41 </div>
42 );
43 },
44 { displayName: 'Hello' },
45);
46
47const Page = observer(
48 (props) => {
49 return <div>{props.children}</div>;
50 },
51 { displayName: 'Page' },
52);
53
54export default () => {
55 return (
56 <SchemaComponentProvider components={{ FormItem, Page, Hello }}>
57 <SchemaComponent
58 schema={{
59 type: 'void',
60 name: 'page',
61 'x-component': 'Page',
62 properties: {
63 hello1: {
64 type: 'string',
65 title: 'Title 1',
66 'x-decorator': 'FormItem',
67 'x-component': 'Hello',
68 },
69 },
70 }}
71 />
72 </SchemaComponentProvider>
73 );
74};