对 Schema 节点进行增、删、改操作,并且提供了事件触发机制,用于将数据同步到服务端。
interface Options {
current: Schema;
api?: APIClient;
onSuccess?: any;
refresh?: () => void;
t?: any;
}
interface InsertAdjacentOptions {
wrap?: (s: ISchema) => ISchema;
removeParentsIfNoChildren?: boolean;
breakRemoveOn?: ISchema | BreakFn;
onSuccess?: any;
}
class Designable {
constructor(options: Options ) { }
loadAPIClientEvents(): void;
on(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', listener: any): void
emit(name: 'insertAdjacent' | 'remove' | 'error' | 'patch' | 'batchPatch', ...args: any[]): Promise<any>
refresh(): void
recursiveRemoveIfNoChildren(schema?: Schema, options?: RecursiveRemoveOptions): Schema;
remove(schema?: Schema, options?: RemoveOptions): Promise<any>
removeWithoutEmit(schema?: Schema, options?: RemoveOptions): Schema
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
insertBeforeBeginOrAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
insertBeforeBegin(schema: ISchema, options?: InsertAdjacentOptions): void
insertAfterBegin(schema: ISchema, options?: InsertAdjacentOptions): void
insertBeforeEnd(schema: ISchema, options?: InsertAdjacentOptions): Promise<any>
insertAfterEnd(schema: ISchema, options?: InsertAdjacentOptions): void
}
参数讲解
current
:需要操作的 Schema 节点api
:用于发起后端请求的 APIClient 实例onSuccess
:后端接口请求成功后的回调refresh
:用于更新节点后,刷新页面t
:useTranslation()
的返回值示例
const schema = new Schema({
type: 'void',
name: 'hello',
'x-component': 'div',
})
const dn = new Designable({ current: schema });
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
}
}
}
}
});
const b = schema.b;
const dn = createDesignable({
current: b,
})
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
移除当前节点
dn.remove();
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
dn.remove();
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
{
type: 'void',
name: 'a',
properties: {
- b: {
- type: 'void',
- properties: {
- c: {
- type: 'void',
- }
- }
- }
}
}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
dn.insertBeforeBegin({
type: 'void',
name: 'd',
});
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
dn.insertBeforeBegin({
type: 'void',
name: 'd',
});
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
{
type: 'void',
name: 'a',
properties: {
+ d: {
+ type: 'void',
+ },
b: {
type: 'void',
properties: {
c: {
type: 'void',
}
}
}
}
}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
dn.insertAfterBegin({
type: 'void',
name: 'd',
});
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
dn.insertAfterBegin({
type: 'void',
name: 'd',
});
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
{
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
+ d: {
+ type: 'void',
+ },
c: {
type: 'void',
}
}
}
}
}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
dn.insertBeforeEnd({
type: 'void',
name: 'd',
});
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
dn.insertBeforeEnd({
type: 'void',
name: 'd',
});
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
{
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
+ d: {
+ type: 'void',
+ },
}
}
}
}
在当前节点的前面插入,并会触发 insertAdjacent
事件。
dn.insertAfterEnd({
type: 'void',
name: 'd',
});
console.log(schema.toJSON());
import React from 'react';
import { Schema } from '@tachybase/schema';
import { createDesignable } from '@tachybase/client';
const schema = new Schema({
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
},
},
},
},
});
const b = schema.properties['b'];
const dn = createDesignable({
current: b,
});
dn.insertAfterEnd({
type: 'void',
name: 'd',
});
export default () => <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>;
{
type: 'void',
name: 'a',
properties: {
b: {
type: 'void',
properties: {
c: {
type: 'void',
}
}
},
+ d: {
+ type: 'void',
+ },
}
}
根据第一个参数决定插入的位置,是前面四个方法的封装。
class Designable {
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void | Promise<any>
}
on
:添加事件监听的基础方法loadAPIClientEvents
:调用 on
方法添加对 insertAdjacent
、patch
、batchPatch
、remove
的事件的监听,主要功能是将变更的 Schema 更新到服务端emit
:是根据事件名称,调用之前注册过的方法,具体是由前面讲过的 插入操作和删除操作 触发而 loadAPIClientEvents()
并非在初始化时调用,需要手动调用,换而言之,如果不调用 dn.loadAPIClientEvents()
,则不会将更新发送到服务端,主要是简化在单测或者 DEMO 环境对服务端的 Mock。
对 new Designable()
的简单封装。
function createDesignable(options: CreateDesignableProps) {
return new Designable(options);
}
const dn = createDesignable({ current: schema });
用户获取当前节点 Schema JSON 对象,更多信息请参考 formily useFieldSchema()。
import { Schema } from '@tachybase/schema';
const useFieldSchema: () => Schema;
/**
* defaultShowCode: true
*/
import React from 'react';
import { useFieldSchema } from '@formily/react';
import { Application, Plugin, SchemaComponent } from '@tachybase/client';
const Demo = ({ children }) => {
const fieldSchema = useFieldSchema();
return <div style={{ border: '1px solid red' }}>
<pre>{ JSON.stringify(fieldSchema, null, 2)}</pre>
<div style={{ paddingLeft: 20 }}>{children}</div>
</div>
}
const schema = {
type: 'void',
name: 'hello',
'x-component': 'Demo', // 这里是 Demo 组件
'properties': {
'world': {
'type': 'void',
'x-component': 'Demo', // 这里也是 Demo 组件
},
}
}
const Root = () => {
return <SchemaComponent components={{ Demo }} schema={schema} />
}
const app = new Application({
providers: [Root]
})
export default app.getRootComponent();
获取当前节点 Schema 实例,更多信息请参考 formily useField()
import { GeneralField } from '@formily/core';
const useField: <T = GeneralField>() => T;
const Demo = () => {
const field = useField();
console.log('field', field);
return <div></div>
}
const schema = {
type: 'void',
name: 'hello',
'x-component': 'Demo', // 这里是 Demo 组件
'properties': {
'world': {
'type': 'void',
'x-component': 'Demo', // 这里也是 Demo 组件
},
}
}
const Root = () => {
return <SchemaComponent components={{ Hello }} schema={schema} />
}
对当前 Schema 节点的修改操作。
interface InsertAdjacentOptions {
wrap?: (s: ISchema) => ISchema;
removeParentsIfNoChildren?: boolean;
breakRemoveOn?: ISchema | BreakFn;
onSuccess?: any;
}
type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
function useDesignable(): {
dn: Designable;
designable: boolean;
reset: () => void;
refresh: () => void;
setDesignable: (value: boolean) => void;
findComponent(component: any): any;
patch: (key: ISchema | string, value?: any) => void
on(name: "error" | "insertAdjacent" | "remove" | "patch" | "batchPatch", listener: any): void
remove(schema?: any, options?: RemoveOptions): void
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions): void
insertBeforeBegin(schema: ISchema): void;
insertAfterBegin(schema: ISchema): void;
insertBeforeEnd(schema: ISchema): void;
insertAfterEnd(schema: ISchema): void;
}
详细解释
Designable
的实例null
dn.remove
方法dn.on
方法dn.insertAdjacent
方法
dn.insertBeforeBegin
方法dn.insertAfterBegin
方法dn.insertBeforeEnd
方法dn.insertAfterEnd
方法示例
插入节点。
import React from 'react';
import {
SchemaComponentProvider,
SchemaComponent,
useDesignable,
} from '@tachybase/client';
import { observer, Schema, useFieldSchema } from '@formily/react';
import { Button, Space } from 'antd';
import { uid } from '@formily/shared';
const Hello = observer(
(props) => {
const { insertAdjacent } = useDesignable();
const fieldSchema = useFieldSchema();
return (
<div>
<h1>{fieldSchema.name}</h1>
<Space>
<Button
onClick={() => {
insertAdjacent('beforeBegin', {
'x-component': 'Hello',
});
}}
>
before begin
</Button>
<Button
onClick={() => {
insertAdjacent('afterBegin', {
'x-component': 'Hello',
});
}}
>
after begin
</Button>
<Button
onClick={() => {
insertAdjacent('beforeEnd', {
'x-component': 'Hello',
});
}}
>
before end
</Button>
<Button
onClick={() => {
insertAdjacent('afterEnd', {
'x-component': 'Hello',
});
}}
>
after end
</Button>
</Space>
<div style={{ margin: 50 }}>{props.children}</div>
</div>
);
},
{ displayName: 'Hello' },
);
const Page = observer(
(props) => {
return <div>{props.children}</div>;
},
{ displayName: 'Page' },
);
export default () => {
return (
<SchemaComponentProvider components={{ Page, Hello }}>
<SchemaComponent
schema={{
type: 'void',
name: 'page',
'x-component': 'Page',
properties: {
hello1: {
type: 'void',
'x-component': 'Hello',
},
},
}}
/>
</SchemaComponentProvider>
);
};
部分更新。
import React from 'react';
import {
SchemaComponentProvider,
SchemaComponent,
FormItem,
useDesignable,
} from '@tachybase/client';
import { observer, Schema, useField, useFieldSchema, uid } from '@tachybase/schema';
import { Button } from 'antd';
const Hello = observer(
(props) => {
const fieldSchema = useFieldSchema();
const field = useField();
const { dn } = useDesignable();
return (
<div>
<h1>{field.title}</h1>
{ JSON.stringify(props) }
<br/>
{ JSON.stringify(field.componentProps) }
<br/>
{ JSON.stringify(field.decoratorProps) }
<br/>
{ JSON.stringify(fieldSchema.toJSON()) }
<br/>
<Button onClick={() => {
dn.shallowMerge({
title: uid(),
'x-component-props': {a: uid(), },
'x-decorator-props': {b: uid(), },
});
}}>shallowMerge</Button>
<Button onClick={() => {
dn.deepMerge({
title: uid(),
'x-component-props': {c: uid() },
'x-decorator-props': {d: uid(), },
});
}}>deepMerge</Button>
</div>
);
},
{ displayName: 'Hello' },
);
const Page = observer(
(props) => {
return <div>{props.children}</div>;
},
{ displayName: 'Page' },
);
export default () => {
return (
<SchemaComponentProvider components={{ FormItem, Page, Hello }}>
<SchemaComponent
schema={{
type: 'void',
name: 'page',
'x-component': 'Page',
properties: {
hello1: {
type: 'string',
title: 'Title 1',
'x-decorator': 'FormItem',
'x-component': 'Hello',
},
},
}}
/>
</SchemaComponentProvider>
);
};