Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/dynamic schema #60 #62

Merged
merged 3 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
});
Loading