Wen Chen
Published on

Ant Design - 光速打造你的管理系統

Authors
  • avatar
    Name
    Wen Chen

前言

過去幾個月時間,把公司產品供客戶使用的管理後台用 Ant Design 算是完整重構了。

得益於 Antd 的強大,讓我們可以用很短的時間將整個管理系統翻新,心中其實一直想為它寫些什麼, 不過因為 Antd 的 官方文件 實在太完整了,幾乎所有想要的資訊都可以在上面找到,本篇主要會著重在使用經驗與分享,畢竟,它確實值得我大力推廣XD。

備註:我們使用的版本是 4.23.1,底下皆以這版本為主,某些內容在後續版本可能會獲得更新

Ant Design ?

Ant Design 是由螞蟻集團所推出的用來建構後台系統用的 UI Library,在 Github 上目前有 84.4k 顆星星,而用來做後台的 UI Library 那麼多,Antd 到底有什麼不同?

根據這次負責的專案,功能包含了,「 大量複雜的表單、大量的 Date Picker、Progress、各式各樣的 Table (樹狀、可編輯、排序、分頁) 」,絕大多數的 UI Library,通常只支援 UI 的部分,元件功能上,都需要自己寫或是再另外找 Library 來完成特定需求。

而 Antd 便是符合上述需求的不二人選,使用這幾個月我認為它有一些很大的優點:

  1. 提供 React Component,且提供的元件種類超乎想像,幾乎沒有遇到無法支援的情境
  2. Antd 提供的 Form 與其他 Antd 組件搭配非常順暢,提供的 api 也很易用
  3. 文件非常齊全,不管中文英文都很詳細,幾乎不需要再看教學,照文件做就好
  4. 更新頻繁,不怕當孤兒

為佐證上面的優點,我們來看下最管理後台使用最頻繁的 Table 文件

table

光是右側 Example 就提供一~大堆可以參考,每一個都有實際畫面可以操作,功能非常齊全,而且本身設計的就很舒服,極少數情況才需要自幹改樣式,大部分頂多改改寬高、padding、margin 就很夠用了,就算真的需要 override css 也不會太難改。

快速上手重點功能 !

上面簡單介紹了 Antd ,接下來會介紹幾種個人覺得使用 Antd 必用到的功能。

Form

表單是後台管理系統不可或缺的功能,Antd 提供了非常強大的 Form 供使用者使用,在使用上和各元件的整合度也非常高,先上範例:CodeSandBox

簡單把玩過範例後,底下擷取片段說明,完整程式碼請看 CodeSandBox 內:

import { Button, Form, Input, Select } from 'antd';
import React from 'react';

const { Option } = Select;

const App: React.FC = () => {
  const [form] = Form.useForm();

  const onGenderChange = (value: string) => {
    switch (value) {
      case 'male':
        form.setFieldsValue({ note: 'Hi, man!' });
        break;
      case 'female':
        form.setFieldsValue({ note: 'Hi, lady!' });
        break;
      case 'other':
        form.setFieldsValue({ note: 'Hi there!' });
        break;
      default:
    }
  };

  const onReset = () => {
    form.resetFields();
  };

  return (
    <Form
      form={form}
      name="control-hooks"
    >
      <Form.Item name="note" label="Note" rules={[{ required: true }]}>
        <Input />
      </Form.Item>
      <Form.Item name="gender" label="Gender" rules={[{ required: true }]}>
        <Select
          placeholder="Select a option and change input text above"
          onChange={onGenderChange}
          allowClear
        >
          <Option value="male">male</Option>
          <Option value="female">female</Option>
          <Option value="other">other</Option>
        </Select>
      </Form.Item>

      <Form.Item >
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
        <Button htmlType="button" onClick={onReset}>
          Reset
        </Button>
      </Form.Item>
    </Form>
  );
};

export default App;

首先我們分段來解析上面的 Code :

const [form] = Form.useForm() : 我們使用上會用 antd 提供的 hook 產出一個 form 實例,方便我們傳遞於各組件,以及管理所有數據狀態,使用方式也很易用,只需要傳入 Form 元件本身即可。

 const [form] = Form.useForm();

  return (
    <Form form={form}>
      <Sub />
    </Form>
  );

Form.Item : 用來將 InputSelect 等各種元件與 Form 綁定數據:

  • name :用來表單 form 表單的 key。
  • label :畫面顯示的欄位標籤。
  • rule:規則檢查,此處展示必填規則。
<Form.Item name="note" label="Note" rules={[{ required: true }]}>
        <Input />
</Form.Item>

少數元件需綁定 valuePropName,才可正確吃到資料,如 Switch 或 CheckBox 需綁定 valuePropName='checked'

// Switch
<Form.Item name='status' label="Status" valuePropName='checked'>
    <Switch />
</Form.Item>

資料管理

完成元件之後,useForm 創建出來的 FormInstance 本身提供很多 api 供資料與狀態管理,底下提幾種常用到的:

setFieldsValuesetFieldValue :手動設置 Form 表單內的值,絕大多數會使用 setFieldsValue 可以一次設定多個:

 const onGenderChange = (value: string) => {
    switch (value) {
      case 'male':
        form.setFieldsValue({ note: 'Hi, man!' });
        break;
      case 'female':
        form.setFieldsValue({ note: 'Hi, lady!' });
        break;
      case 'other':
        form.setFieldsValue({ note: 'Hi there!' });
        break;
      default:
    }
  };

form.resetFields : 預設會 reset 整個 form ,亦可以傳入 NamePath[] 來 reset 特定欄位。

假如有設置 initialValues,會重置寫在 Form 元件之中的 initialValues 而非單純清除。

const onReset = () => {
    form.resetFields(NamePath[])
  };

接下來我們來講講資料提取的部分:

getFieldValue : getFieldValue([NamePath]) 取得指定 name 的值,使用率不高,但偶爾有奇效。

getFieldsValue : 大部分會使用 getFieldsValue() 來默認拿到當前 form 裡面現存的值。

const data = form.getFieldsValue()

// 或直接解構使用
const { note , gender } = form.getFieldsValue()

Tips : 要注意,默認拿取現存的值,預設情況下,只會拿到 mounted 的值,不出現在畫面的值是拿不到的
可以透過加上 true 來強制取得所有存於 form 內的資料 form.getFieldsValue(true)

validateFields : 會回傳一個 promise,用來驗證表單時非常好用,成功時會回傳 form 裡的資料,假如有未通過的欄位會自動 reject。

// return sample
validateFields()
  .then((values) => {
    /*
  values:
    {
      username: 'username',
      password: 'password',
    }
  */
  })
  .catch((errorInfo) => {
    /*
    errorInfo:
      {
        values: {
          username: 'username',
          password: 'password',
        },
        errorFields: [
          { name: ['password'], errors: ['Please input your Password!'] },
        ],
        outOfDate: false,
      }
    */
  });

以上便是比較常用到的資料獲取方式,值得一提的是,getFieldValue 不像 getFieldsValue 預設只能拿到 mounted 的資料,所以某些時候也可以透過 getFieldValue 來取得沒有 mounted 的 formData

Form.useWatch : 一樣是很好用的 hooks 之一,放入要監聽的 name 和 formInstance,Antd 會自動監聽 form 的變化,可以將它視為一個 state,可以拿來更新畫面、甚至是和其他 hooks 組合,使用方式:

// type Form.useWatch = (namePath: NamePath, formInstance?: FormInstance): Value
const [form] = Form.useForm();
const userName = Form.useWatch('username', form);

// 與 useSWR 組合
const { data: options } = useSWR(`/api/user/${userName}`, fetcher);

// 與 useEffect 組合

useEffect(() => {
    // ... do something
} , [userName])

Tips:一樣只能監聽 mounted 的資料!


Table

和表單一樣重要的當然是渲染資料的 Table,不囉唆一樣先上官方範例:

const dataSource = [
  {
    key: '1',
    name: 'Mike',
    age: 32,
    address: '10 Downing Street',
  },
  {
    key: '2',
    name: 'John',
    age: 42,
    address: '10 Downing Street',
  },
];

const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    key: 'address',
  },
];

// component
<Table dataSource={dataSource} columns={columns} />;

從上面範例可以看到 Table 元件本身有兩大重點要傳入:

dataSource:要傳入 Table 的資料

columns:Table 的 columns,完整參數可看這裡,和 dataSource 一樣是陣列結構,每個物件即代表一個 column,裡面比較重要的參數有:

  1. title : column title。
  2. key 每個 column 的 unique key,官方文件上有提到,假如有指定 dataIndex 的話可以省略。
  3. dataIndex : 有給的話會去抓 dataSource 內物件的指定 Object Key,一個蘿菠一個坑,也可以不寫,待會會介紹到。

Table 同時也支援透過 render 來自定義要顯示的內容,不一定只能秀文字,可以是tag 或是任何東西:

// render type : function(text, record, index) {}

const columns = [
    {
        title:"Tags",
        dataIndex:"tags",
        key:"tags",
        render:(tags: string[]) => (
        <>
          {tags.map((tag) => (
            <Tag color="blue" key={tag}>
              {tag}
            </Tag>
          ))}
        </>
      )}]

render必須要回傳一個 ReactNode,而傳入的 params 預設就是 dataIndex 綁定的值,前面有提到,可以不給 dataIndex,這樣便會直接傳入整個 record,可以用在該欄位可能需要不同資料來搭配呈現時使用。

這個CodeSandBox有比較完整的 render 使用,可以看看。

Tips : 使用上,我們 dataSource 通常會直接拿 api 回傳的 response 塞進去,但 response 不一定會帶 key,然而 dataSource 的 key 在這裡是必要的,假如沒給會噴 key warning,這點要注意,當初找這個警告來源找好久QQ

後記

其實原本真的只是想簡單寫個介紹,畢竟官方文件資源真的很豐富,沒想到寫下去才發現有一堆小細節可以分享,礙於篇幅(其實是寫累了),決定把一些更細節的東西放在未來的文章內~

總之,這還是一篇介紹 (推坑) 文,在這段時間的接觸中,雖然也出現過幾次 bug ,但 Antd 本身的更新很頻繁,更新套件版本後都有得到解決,總之目前來說,是個有在維護的項目!不用怕壞了沒人處理!

有需要製作管理後台的朋友們,不仿可以試試看!