Skip to content

Commit

Permalink
[WIP]doc: develope array-items (#720)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnIsOnTheRoad authored Mar 13, 2020
1 parent 130feea commit ffdfdc2
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 7 deletions.
236 changes: 235 additions & 1 deletion docs/zh-cn/schema-develop/self-inc-component.md
Original file line number Diff line number Diff line change
@@ -1 +1,235 @@
# 实现自增列表组件
# 实现自增列表组件

`Formily` 已经内置了 `array-table``array-cards` 两种类型的自增列表组件。开发者可以非常方便的使用它们。
如果内置类型已经无法满足特有业务需求,那这篇文档将帮助你快速实现定制化的自增列表组件。

> 内置类型源码阅读:
* Fusion-Next: [array-cards](https://github.com/alibaba/formily/tree/master/packages/next-components/src/array-cards)[array-table](https://github.com/alibaba/formily/tree/master/packages/next-components/src/array-table)
* Ant-Design [array-cards](https://github.com/alibaba/formily/tree/master/packages/antd-components/src/array-cards)[array-table](https://github.com/alibaba/formily/tree/master/packages/antd-components/src/array-table)

## 简单版本

这个版本旨在快速讲清楚如何实现自增组件,仅需要 **25** 行代码就能实现一个 `Formily` 自增组件。可以点击 [IMutators](#IMutators) 了解更多API。

```tsx
import { Button } from 'antd'
import { SchemaForm, SchemaField, toArr, FormPath } from '@formily/antd'

const ArrayCustom = (props) => {
const { value, schema, className, editable, path, mutators } = props;
const componentProps = schema.getExtendsComponentProps() || {}
const onAdd = () => mutators.push(schema.items.getEmptyValue())
const onRemove = (index) => mutators.remove(index)
const onMoveUp = (index) => mutators.moveUp(index)
const onMoveDown = (index) => mutators.moveDown(index)

return <div>
{toArr(value).map((item, index) => (
<div {...componentProps} key={index}>
<SchemaField path={FormPath.parse(path).concat(index)} />
<Button onClick={onRemove.bind(null, index)}>remove</Button>
<Button onClick={onMoveUp.bind(null, index)}>up</Button>
<Button onClick={onMoveDown.bind(null, index)}>down</Button>
</div>
))}
<Button onClick={onAdd}>add</Button>
</div>
}
ArrayCustom.isFieldComponent = true
```

## 生产级别版本

为了编写更**健壮**的自增列表组件,我们会用到 `@formily/react-shared-components`[ArrayList](https://github.com/alibaba/formily/blob/master/packages/react-shared-components/src/ArrayList.tsx)

## 认识ArrayList

**ArrayList** 已经内置了通用的自增列表逻辑,只需要配置它们即可。

```tsx
<ArrayList
value={value}
minItems={schema.minItems}
maxItems={schema.maxItems}
editable={editable}
components={{
CircleButton: (props) => <Button {...props} />, // moveup默认使用CircleButton
TextButton: (props) => <Button text {...props} />, // 添加默认使用TextButton
AdditionIcon: () => <div>+</div>, // 自定义添加icon
RemoveIcon: () => <div>x</div>, // 自定义删除icon
MoveDownIcon: () => <div>↓</div>, // 自定义下移icon
MoveUpIcon: () => <div>↑</div>, // 自定义上移icon
}}
renders={{ // 拓展给使用者的渲染方法
renderAddition, // 对应的组件为 ArrayList.Addition
renderRemove, // 对应的组件为 ArrayList.Remove
renderMoveDown, // 对应的组件为 ArrayList.MoveDown
renderMoveUp, // 对应的组件为 ArrayList.MoveUp
renderEmpty // 对应的组件为 ArrayList.Empty
}}
/>
```

## 生产级别版本实践

以下例子展示了如何使用 **ArrayList** 进行快速编写自增组件,以及对每一行自增组件样式的控制。

```jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Button } from 'antd'
import styled from 'styled-components'
import { SchemaForm, SchemaField, SchemaMarkupField as Field } from '@formily/antd'
import { ArrayList } from '@formily/react-shared-components'
import { toArr, isFn, FormPath } from '@formily/shared'
import { Input } from '@formily/antd-components'
import'antd/dist/antd.css'

const ArrayComponents = {
CircleButton: (props) => <Button {...props} />,
TextButton: (props) => <Button text {...props} />,
AdditionIcon: () => <div>+Add</div>,
RemoveIcon: () => <div>Remove</div>,
MoveDownIcon: () => <div>Down</div>,
MoveUpIcon: () => <div>Up</div>,
}

const RowStyleLayout = styled((props) => <div {...props} />)`
.ant-btn {
margin-right: 16px;
}
.ant-form-item {
display: inline-flex;
margin-right: 16px;
margin-bottom: 16px;
}
> .ant-form-item {
margin-bottom: 0;
margin-right: 0;
}
`

const ArrayCustom = (props) => {
const { value, schema, className, editable, path, mutators } = props;
const {
renderAddition,
renderRemove,
renderMoveDown,
renderMoveUp,
renderEmpty,
renderExtraOperations,
...componentProps
} = schema.getExtendsComponentProps() || {}

const onAdd = () => {
const items = Array.isArray(schema.items)
? schema.items[schema.items.length - 1]
: schema.items
mutators.push(items.getEmptyValue())
}

return <ArrayList
value={value}
minItems={schema.minItems}
maxItems={schema.maxItems}
editable={editable}
components={ArrayComponents}
renders={{
renderAddition,
renderRemove,
renderMoveDown,
renderMoveUp,
renderEmpty, // 允许开发者覆盖默认
}}
>
{toArr(value).map((item, index) => {
return (
<RowStyleLayout {...componentProps} key={index}>
<span>#{index + 1}. </span>
<SchemaField path={FormPath.parse(path).concat(index)} />
<ArrayList.Remove index={index} onClick={() => mutators.remove(index)} />
<ArrayList.MoveDown index={index} onClick={() => mutators.moveDown(index)} />
<ArrayList.MoveUp index={index} onClick={() => mutators.moveUp(index)} />
</RowStyleLayout>
)
})}
<ArrayList.Empty>
{({ children }) => {
return (
<div
{...componentProps}
size="small"
className={`card-list-item card-list-empty`}
onClick={onAdd}
>
<div>{children}</div>
</div>
)
}}
</ArrayList.Empty>
<ArrayList.Addition>
{({ children, isEmpty }) => {
if (!isEmpty) {
return (
<div className="array-cards-addition" onClick={onAdd}>
{children}
</div>
)
}
}}
</ArrayList.Addition>
</ArrayList>
}

ArrayCustom.isFieldComponent = true

const App = () => {
return (
<SchemaForm
components={{
ArrayCustom,
Input,
}}
>
<Field
title="用户列表"
name="userList"
type="array"
default={[
{ username: 'molly', age: 20 },
{ username: 'joe', age: 21 }
]}
x-component="ArrayCustom"
>
<Field type="object">
<Field name="username" x-component="Input" title="用户名" />
<Field name="age" x-component="Input" title="年龄" />
</Field>
</Field>
</SchemaForm>
)
}

ReactDOM.render(<App />, document.getElementById('root'))

```

### IMutators

| 属性名 | 说明 | 类型 | 默认值 |
|:----------|:---------------------------------|:--------------------|:--------------------|
| change | 改变当前行的值 | change(...values: any[]): any | |
| focus | 聚焦 | | |
| blur | 失焦 | | |
| push | 增加一行数据 | (value?: any): any[] | |
| pop | 弹出最后一行 | change(...values: any[]): any | |
| insert | 插入一行数据 | (index: number, value: any): any[] | |
| remove | 删除某一行 | (index: number | string): any | |
| unshift | 插入第一行数据 | (value: any): any[] | |
| shift | 删除第一行是数据 | (): any[] | |
| exist | 是否存在某一行 | (index?: number | string): boolean | |
| move | 将指定行数据移动到某一行 | ($from: number, $to: number): any[] | |
| moveDown | 将某一行往下移 | (index: number): any[] | |
| moveUp | 将某一行往上移 | (index: number): any[] | |
| validate | 执行校验 | (opts?: IFormExtendedValidateFieldOptions): Promise<IFormValidateResult> | |

1 change: 1 addition & 0 deletions packages/antd-components/src/array-cards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const ArrayCards: any = styled(
renderExtraOperations,
...componentProps
} = schema.getExtendsComponentProps() || {}

const onAdd = () => {
const items = Array.isArray(schema.items)
? schema.items[schema.items.length - 1]
Expand Down
4 changes: 1 addition & 3 deletions packages/antd/README.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ npm install --save @formily/antd
| 参数 | 说明 | 类型 | 默认值 |
|:----------|:---------------------------------|:--------------------|:--------------------|
| schema |通过schema渲染表单 | [ISchema](#ISchema) | |
| fields |传入自定义表单组件 | { [key: string]: [ISchemaFieldComponent](#ISchemaFieldComponent) } | |
| virtualFields |传入自定义虚拟组件 | { [key: string]: [ISchemaVirtualFieldComponent](#ISchemaVirtualFieldComponent) } | |
| formComponent |全局注册Form渲染组件 | string `or` React.ReactElement | |
| formItemComponent |全局注册FormItem渲染组件 | React.ReactElement | |
| labelCol |label布局控制 | number `or` { span: number; offset?: number } | |
| wrapperCol |FormItem布局控制 | number `or` { span: number; offset?: number } | |
| previewPlaceholder |自定义预览placeholder | string `or` ((props: [IPreviewTextProps](#IPreviewTextProps)) => string) | |
| previewPlaceholder |详情页的文本态占位符 | string `or` ((props: [IPreviewTextProps](#IPreviewTextProps)) => string) | N/A |
| prefix |样式前缀 | string | |
| inline |是否为内联表单 | boolean | |
| size |单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。 | 'large' `or` 'medium' `or` 'small' | |
Expand Down
4 changes: 1 addition & 3 deletions packages/next/README.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,9 @@ ReactDOM.render(<App />, document.getElementById('root'))

| 参数 | 说明 | 类型 | 默认值 |
|:----------|:---------------------------------|:--------------------|:--------------------|
| fields |传入自定义表单组件 | { [key: string]: [ISchemaFieldComponent](#ISchemaFieldComponent) } | |
| virtualFields |传入自定义虚拟组件 | { [key: string]: [ISchemaVirtualFieldComponent](#ISchemaVirtualFieldComponent) } | |
| labelCol |label布局控制 | number `or` { span: number; offset?: number } | |
| wrapperCol |FormItem布局控制 | number `or` { span: number; offset?: number } | |
| previewPlaceholder |自定义预览placeholder | string `or` ((props: [IPreviewTextProps](#IPreviewTextProps)) => string) | |
| previewPlaceholder |详情页的文本态占位符 | string `or` ((props: [IPreviewTextProps](#IPreviewTextProps)) => string) | N/A |
| prefix |样式前缀 | string | |
| inline |是否为内联表单 | boolean | |
| size |单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。 | 'large' `or` 'medium' `or` 'small' | |
Expand Down

0 comments on commit ffdfdc2

Please sign in to comment.