Skip to content

Commit

Permalink
Merge pull request #62 from drdevelop/feat/dynamic_schema
Browse files Browse the repository at this point in the history
Feat/dynamic schema #60
  • Loading branch information
drdevelop authored Nov 19, 2023
2 parents e0c2727 + e98105f commit e4cde0b
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
88 changes: 88 additions & 0 deletions docs/schemaForm/hydrate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
nav:
title: schema表单
group: 进阶使用
title: 服务端动态维护schema
order: 6
toc: content
---

## schema转换成字符串存入服务端

```tsx
/**
* title: 调用stringifySchema方法将schema对象转成字符串
*/
import React, { useState } from 'react';
import { Form, Button } from 'antd'
import SchemaForm, { stringifySchema } from 'antd-pro-schema-form';

export default () => {
const [form] = Form.useForm();
const [schemaStr, setSchemaStr] = useState();

const schema = [{
fieldName: 'schema',
label: 'Schema',
type: 'textarea',
mapStateToSchema(globalState) { // 这种方式stringify后的parse无法还原函数,只有箭头函数才行
return { data: globalState };
}
}];

const onSubmit = () => {
const schemaObj = new Function(`return (${form.getFieldsValue().schema})`)();
setSchemaStr(stringifySchema(schemaObj)); // 转换schema对象为字符串
};

return (
<>
<SchemaForm form={form} schema={schema} />
<Button type="primary" onClick={onSubmit}>
一键提取服务端可存储的schema
</Button>
<div style={{ color: 'red' }}>服务端schema:</div>
<div>{schemaStr}</div>
</>
)
}
```


## 将服务端schema字符串格式转换成对象模式

```tsx
/**
* title: 调用parseSchema方法将schema字符串转成SchemaForm可识别的对象schema格式
*/
import React, { useState, useMemo } from 'react';
import { Form, Button } from 'antd'
import SchemaForm, { parseSchema } from 'antd-pro-schema-form';

export default () => {
const [form] = Form.useForm();

const [parsedSchema, setParsedSchema] = useState();
const schema = [{
fieldName: 'schema',
label: '服务端Schema',
type: 'textarea',
}];

const onSubmit = () => {
setParsedSchema(parseSchema(form.getFieldValue('schema')));
}

return (
<>
<h2>输入服务端schema字符串:</h2>
<SchemaForm form={form} schema={schema} />
<Button type="primary" onClick={onSubmit}>
一键解析服务端的schema字符串并渲染
</Button>
<h2>解析后的schema渲染后的表单: </h2>
{ parsedSchema && <SchemaForm schema={parsedSchema} /> }
</>
)
}
```
2 changes: 2 additions & 0 deletions packages/form/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export type SchemaGroups<T = ''> = Props<T>['schemaGroups'];
export { default } from './core';

export { registerComponent, registerComponents } from './components';

export { stringifySchema, parseSchema } from './shared/schemaTool';
8 changes: 8 additions & 0 deletions packages/form/src/shared/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ export function isArray(value: any) {
return (value instanceof Array);
}

export function isRegExp(value: any) {
return Object.prototype.toString.call(value) === '[object RegExp]';
}

export function isFunction(value: any) {
return typeof value === 'function';
}

export function genIndexArrayByLen(len: number) {
const arr: number[] = [];
for (let i = 0; i < len; i++) {
Expand Down
59 changes: 59 additions & 0 deletions packages/form/src/shared/schemaTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Schema } from '../core/types';
import { isFunction, isRegExp } from './helper';

function replacer(key: string, value: any) {
if (isFunction(value)) {
const fnStr = value.toString().replaceAll('\n', '');
// case1: { a: () => 1 }
// case2: { a() {} }
// case3: { a: function() {} }
// case4: { a: function() { return () => 1 }}
return (
/^\s?\([^;]*?\)\s?=>/.test(fnStr) || /^\s?function/.test(fnStr)
? fnStr
: 'function ' + fnStr
);
}
if (isRegExp(value)) {
return value.toString();
}
return value;
}

export function stringifySchema(schema: Schema<any>) {
try {
return JSON.stringify(schema, replacer, 2).replaceAll('\\n', '\n');
} catch (err) {
console.error('stringify error', err);
return schema;
}
}

function reviver(key: string, value: any) {
try {
// eslint-disable-next-line
const computedVal = new Function(`return (${value})`)();
if (isFunction(computedVal) || isRegExp(computedVal)) {
return computedVal;
} else {
return value;
}
} catch (err) {
return value;
}
}

// TODO: XSS defense
/**
* parse schema from server
* !!!Don't use ToC scene,Because you will definitely encounter css attacks,
* Unless you do some encode for schema str
*/
export function parseSchema(schema: string) {
try {
return JSON.parse(schema, reviver);
} catch (err) {
console.error('parse error', err);
return schema;
}
}
28 changes: 28 additions & 0 deletions packages/form/tests/schema/serverMaintain.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import SchemaForm, { stringifySchema, parseSchema } from '../../src';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';

it('stringify schema', () => {
// eslint-disable-next-line
// @ts-ignore
// eslint-disable-next-line
expect(stringifySchema({a: 1})).toBe(
// eslint-disable-next-line
`{
"a": 1
}`,
);
});

function TestComponent() {
const schema = parseSchema(`
[ { "fieldName": "schema", "label": "schema", "type": "textarea" } ]
`);
return <SchemaForm schema={schema} />;
}

it('parse schema', () => {
render(<TestComponent />);
expect(screen.getByText('schema')).toBeInTheDocument();
});

0 comments on commit e4cde0b

Please sign in to comment.