Ant Design-Form.List

來還債囉。

簡述

這個是在處理表單時我覺得比較麻煩的一個部分,再加上官方範例為了展現所有功能所以寫又更複雜了一些,總之我覺得沒有麼好懂,所以寫一篇筆記來記錄一下。

範例

簡單來說,假設我有一個表單,我希望他送出去時的資料是長這樣:

1
2
3
4
5
6
7
8
9
10
const classRoom = {
number: '1A',
teacher: 'PeaNu',
department: 'CS',
students: [
{ name: 'PPB', phone: '0912345678' },
{ name: 'JoJo', phone: '0912345678' },
{ name: 'Dio', phone: '0912345678' }
]
}

這時候第一個會碰到的問題是:我要怎麼產生 students 的欄位?畢竟我不可能這樣寫:

1
2
3
4
5
6
7
8
<Form.Item label='Students' name='students'>
<Form.Item label='Name' name='name'>
<Input />
</Form.Item>
<Form.Item label='Phone' name='phone'>
<Input />
</Form.Item>
</Form.Item>

這樣子在提交表單的時候只會被當成是新的欄位,像這樣:

wrong-way

附註:注意多了 namephone 這兩個欄位

如果還是不太清楚的話可以到這邊看實際範例

總之呢,這邊要用 antd 提供的 Form.List 來做,先來看寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<Form form={form} initialValues={initRequest} onFinish={onFinish}>
{/* ...略 */}
<Form.List name='students'>
{(fields, operation) => {
return (
<>
{/* 列表欄位 */}
{fields.map((field) => {
return (
<React.Fragment key={field.key}>
<Divider plain>Students</Divider>
<Form.Item label='Name' name={[field.name, 'name']}>
<Input />
</Form.Item>
<Form.Item label='Phone' name={[field.name, 'phone']}>
<Input />
</Form.Item>
{/* 刪除按鈕 */}
<Form.Item>
<Button type='primary' danger onClick={() => operation.remove(field.name)}>
Delete
</Button>
</Form.Item>
</React.Fragment>
)
})}
{/* 新增按鈕 */}
<Form.Item>
<Button onClick={() => operation.add()}>
<PlusOutlined />
</Button>
</Form.Item>
</>
)
}}
</Form.List>
<Form.Item style={{ textAlign: 'center' }}>
<Button type='primary' htmlType='submit'>
Submit
</Button>
</Form.Item>
</Form>

附註:

  • name 裡面的陣列順序一定要是 [field.name, "phone"] 這樣的順序,否則會有問題。(重要!!!)
  • 不要把 onClick={() => operation.add()} 寫成 onClick={operation.add}。雖然這樣可以執行,但會有一些額外的問題。

當初我看到這裡也覺得很亂,不過只要一步一步來看就好。

首先 Form.List 的內容是一個 function,他的內容會長這樣:

1
2
3
4
5
6
7
8
9
10
// function 中的參數
const renderChild = (
fields: Field[],
operation: {
add,
remove,
move,
},
meta: { errors }
) => React.ReactNode

簡單來說就是用它提供給你的「這個 function」來產生內容,至於參數的部分我會一個一個解釋。

fields

這是用來代表列表中的每個欄位,不過它的值可能跟你想得不太一樣,把它印出來的結果是:

fields

之所以會這樣子是因為當我們要渲染 students 時,我們會有多個相同的欄位,像是每個 student 都會有 namephone,這時候如果 fields 也用同樣的名稱的話會「撞名」,所以這邊才會自動產生從 0 遞增的數字來當作欄位名稱。

至於 key 雖然看起來也是從 0 開始遞增的數字,可是有一個很重要的差別是會不停「遞增上去」。

舉例來說,假設我原本有 3 個欄位,現在動態刪除了 1 個欄位,接著再新增 1 個欄位,fields 的內容會變這樣:

field-key

他不像 name 一樣會自動「倒退回去」,而是不停的遞增上去來確保「唯一性」。仔細想想就會覺得這也挺合理的,畢竟是 key 嘛?

這邊也稍微複雜一點,不懂的話到 這邊 自己玩玩看。

operation

這個就比較好理解一些了,簡單來說就是物件中有三個 function:

  • add(defaultValue?: any, insertIndex?: number) 新增一筆欄位(初始值 & 插入位置)
  • remove(index: number | number[]) 傳入指定的值來把欄位刪除(通常會傳 field.name
  • move: (from: number, to: number) 移動欄位的位置

meta

這個是搭配 Form.ErrorList 使用的東西,但我看不太懂官方的 範例 是什麼意思。總之這個應該不太常會用到,只要知道一下是跟錯誤有關的東西就行了。

總之以上看完後你應該就對 Form.List 有一定的理解了,剩下的就多練習吧。

另外如果你想要的是比較單純的資料,像這樣:

1
2
3
{
name: ['name1', 'name2', 'name3']
}

可以參考我寫的簡化版範例

Vue-部署到 GitHub 上的方法 Vue-動態 class 值
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×