一個 Antd 級聯多選組件開發全過程(含源碼地址)

2021-02-20 程式設計師成長指北

Intro

這篇文章將從零開始介紹如何開發一個 Antd 的級聯多選選擇器。先看效果:

Github,Sandbox

閱讀完這篇文章,不僅可以學會如何實現級聯多選的功能,還可以順便學會:

(如果以上內容你都很熟練掌握,繼續閱讀可能不會有太大幫助🧐 )

背景

Ant Design 是阿里開源的,「The world's second most popular React UI framework「,不用多介紹,任何使用 React 開發管理後臺的前端同學肯定都非常熟悉。

Antd 提供了非常多優秀的組件,Button/DatePicker/Form/Cascader/Tree/Notifaction/Modal,太多太多,這些組件組成完整的組件生態,極大程度地提高了普通開發同學的日常開發效率。

然鵝~有些特殊的場景,特殊的需求,可能 Antd 的組件無法支持,也不計劃支持。比如這篇文章提到的級聯多選,從 github 上可以找到很多相關的 issues, Antd 開發者給出的答覆是不支持,或者建議用 TreeSelect。

(貼圖沒有不敬的意思,非常感謝 Antd 的開發者的輸出)

但是作為普通的開發者,一個複雜度如 Tree Select 的組件,可能大多數人一周時間都實現不了(這裡指的是只是 TreeSelect 的所有特性)。更何況在追求敏捷交付,快速試錯的公司根本不可能給你這麼多時間去實現一個看不到即時收益的公共組件。

你肯定也遇到過類似場景,面對產品強硬不可辯駁的需求和社區沒有合適的開源組件時,作為」優秀開源庫使用者「的一絲絲卑微無助😿。

同時,組件實現相關的教程文章也很少,所以這篇文章將介紹如何從零開始動手實現一個級聯多選選擇器。

需求理解

為了讓讀者能深入地了解需求,簡單描述一下(可以在 Sandbox 上動手點一點):

點擊輸入框,在輸入框的上面或者下面展示彈窗,再次點擊彈窗以外的其他區域關閉彈窗

點擊文本展開下一級菜單,點擊 Checkbox 切換選中狀態

Checkbox 有三種狀態,選中(checked)、部分選中(indeterminate)、非選中(unchecked)

支持 Cancel、Confirm 操作,Cancel 關閉彈窗、Confirm 提交選擇。

提交選擇之後,在輸入框內展示父節點的值,也就是Antd 中的 TreeSelect.SHOW_PARENT 策略。

點擊選中項的 x 號可以刪除選中項。

組件設計

在開始動手寫組建的代碼之前,可以先想一下那些不重要的功能邏輯可能會帶來額外的工作量,組件有哪些自己的狀態,可以支持哪些參數。有一份這樣的設計文檔在後續編碼時思路會比較清晰。

前置約定

為了減低複雜度,根據具體需求場景可以做一些前置約定:

只支持多選。(單選呢?當然是用 Antd 的 Cascader 組件)

所有節點的 key 都是字符串,整顆樹範圍內唯一,方便做節點是否被選中的判斷

組件的 value 字符串數組類型,不支持數字或 Symbol 等類型,字符串就可以滿足絕大多數場景

一個節點需要提供的信息有 value(必須,用作唯一標記), title(必須,節點文本展示) 和 children(非必須,子節點數組)

State

級聯多選組件大致需要以下幾個狀態:

忘了在哪裡看過的」能通過計算得到的狀態都應該通過計算得到「的狀態設計原則。在級聯多選組件中,由於最終是以 TreeSelect.SHOW_PARENT 的策略展示,所見即所得,可以讓 value 值也保持一致,下圖中 value 值為 ['深圳市', '荔灣區']。而廣州市,廣東省的部分選中「狀態」是通過計算出來的。

Props

作為一個表單組件,必有的參數就那些,很容易就可以列出來。

PropsTypeDescriptionvaluestring[]數據綁定dataTreeNode[]節點數據 { title: string, value: string, children?: TreeNode }allowClearboolean是否允許清楚placeholderstringPlaceholderonChange(newVal) => voidvalue 變更回調函數classNameboolean額外的 CSS 類名styleReact.CSSProperties額外的樣式disabledboolean是否禁用

支持 value 和 onChange props 就可以在 Antd 的 Form 中使用了。

實現細節Selector 樣式

首先要做的是,表單輸入框,選中節點的標籤 🏷 樣式,由於交付時是作為 Antd 的表單控制項使用。所有要和其他 Select 組件樣式/行為一致。

需要考慮外觀盒模型,hover/ 高亮狀態樣式,支持 allowClear 時的樣式,disabled 的樣式等等。這部分樣式可以自己實現也可以直接從 antd Select 組件上扒樣式。

不過這種情況容易疏漏,擔心有一些沒考慮到的場景。最節省時間成本的方法是,使用 Antd Selector 的類名,直接復用其樣式,偽裝成一個 Selector 🤓 。

彈窗及展開動畫

接下來處理 Selector 的事件,控制菜單的展開和收起。這一步大概要做以下這些事情

給 Selector 綁定監聽事件,當發生點擊時展示菜單

為了不受 Selector 所在的容器及樣式影響,需要使用 Portal 的形式將菜單渲染到整個 React Root Dom 節點的外部

監聽菜單 click outside 事件,事件觸發時關閉菜單

展示菜單和收起菜單時需要有動畫,保持和其他「彈出組件」的統一交互。

最小實現的工作量不大,但,在我們要做級聯多選組件這件事情上不是很重要,可以交給公共的庫去做。在 Antd 的使用的 rc-cascader 組件源碼中可以看到,它的最外層包裹了一個 rc-trigger 的組件,點過去看,果然這個抽象組件容器組件就幫我們做了👆 提到的功能。

return (
<Trigger
...
>
{React.cloneElement(children, {
onKeyDown: this.handleKeyDown,
tabIndex: disabled ? undefined : 0,
})}
</Trigger>
);

rc-xxx 是 Antd 開發團隊提供的基礎組件,我們平時用到的 Antd 組件是在 rc-xxx 組件上的包裝。

使用 rc-trigger,可以非常快速地完成彈窗基本功能。

import { Button, Empty } from 'antd'
import Trigger from 'rc-trigger'

const [popupVisible, setPopupVisible] = useState(false)

return (
<Trigger
action={!disabled ? ['click'] : []}
popup={
<Popup />
}
popupVisible={popupVisible}
onPopupVisibleChange={setPopupVisible}
popupStyle={{
position: 'absolute',
}}
// 對齊方式
popupAlign={{
points: ['tl', 'bl'],
offset: [0, 3]
}}
// 內置動畫
popupTransitionName="slide-up"
>
<Selector
{...props}
/>
</Trigger>
)

rc-trigger 不僅提供了彈窗,對齊方式,點擊觸發方式,甚至連動畫都內置了,只需要設置popupTransitionName="slide-up" 就可以得到和其他 Select 組件一樣的展開動畫。

對於 rc-trigger 組件內部具體是如何實現可以看這篇源碼解讀

Checkbox 🌲 狀態聯動

接下來進入這個組件的重頭戲,Checkbox 🌲 狀態的維護。

以下面的例子來說,深圳市為選中狀態,那麼深圳市下的所有子節點都展示為選中狀態(但不體現在 value 中)。廣州市為部分選中狀態,因為 value 中包含部分廣州市下的區,同理廣東省的半選中狀態也是如此。

結構

考慮到樹結構需要頻繁的向上向下遍歷的操作,我們可能需要的是一個雙向多叉樹的結構🌲 。從父 → 子的聯繫在 children 欄位中已經體現了,還需要給每一個子節點添加一個 parent 屬性指向父節點。

在使用引用計數的垃圾回收機制的語言,循環引用容易出現內存洩漏的問題。而現代的 Javascript 垃圾回收機制是標記清除法。

為了方便判斷通過 value 去獲取對應的節點,在關聯完父 → 子後將樹結構打平,得到一維數組,代碼如下:

export function flattenTree(root: TreeNode[]): TreeNode[] {
const res: TreeNode[] = []

function dfs(nodes: TreeNode[], parent: TreeNode | null = null) {
// ...
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
const { children } = node

const newNode = { ...node, parent }

res.push(newNode)
if (children) { dfs(children, newNode) }
}
// ...
}
dfs(root)

return res
}

Check

當用戶點擊某一級的 Checkbox,需要向上遞歸遍歷所有直接父節點,判斷其子節點是否都為 checked 狀態。如果是,切換 value 為父節點的 value,並刪除其所有子節點 value。

當前 value
['深圳市', '天河區', '荔灣區']

點擊勾選」蘿崗區」 之後,需要加到 value 中
['深圳市', '天河區', '荔灣區', '蘿崗區']

向上遍歷,來到』廣州市『發現所有子節點都被選中了,刪除所有子節點 value,加入自身 value。
['深圳市', '廣州市']

繼續往上判斷,來到廣東省也是所有子節點都被選中了,刪除所有子節點 value,加入自身 value。
['廣東省']

由於沒有更上一層了,向上遍歷中止

代碼如下:

// 狀態提升
export function liftTreeState(
item: TreeNode,
curVal: ValueType[]
): ValueType[] {
const { value } = item

// 加入當前節點 value
const nextValue = curVal.concat(value)
let last = item

// eslint-disable-next-line no-constant-condition
while (true) {
// 如果父節點的所有子節點都已經 checked, 添加該節點 value,繼續嘗試提升
if (
last?.parent?.children!.every((child: TreeNode) => nextValue.includes(child.value))
) {
nextValue.push(last.parent.value)
last = last.parent
} else {
break
}
}
// 移除最後一個滿足 checked 的父節點的所有子孫節點 value
return removeAllDescendanceValue(last, nextValue)
}

UnCheck

當用戶點擊 Uncheck 時,邏輯基本是 Check 操作的反向處理。

取消勾選」荔灣區」 之後,先從該節點一路往上遍歷,一直到當前 value 中的父節點,並暫時保存這條路徑 parentPath ['荔灣區', '廣州市', '廣東省']。

再從"廣東省"一路向下遍歷,如果當前節點在 parentPath 上直接丟棄,如果不在則需要將其放到 nextValue 中。

當前 value
['廣東省', '湖南省']

廣東省在 parentPath,需要丟棄。
['湖南省']

繼續向下遞歸,深圳市不在 parentPath 上,需要加到 value 中,不在 parentPath 上,子節點可以不再遍歷。
['湖南省', '深圳市']

廣州市在 parentPath 上,不在 value 中,繼續遞歸。
['湖南省', '深圳市']

荔灣區在 parentPath 上,不在 value 中,直接忽略,天河區和蘿崗區不在 parentPath 上,需要加入到 value。
['湖南省', '深圳市', '天河區', '蘿崗區']

沒有更深的節點了,遍歷中止

代碼如下:

// 狀態下沉
export function sinkTreeState(root: TreeNode, value: ValueType[]): ValueType[] {
const parentValues: ValueType[] = []
const subTreeValues: ValueType[] = []

// 獲取 parentPath
function getCheckedParent(
node: TreeNode | null | undefined
): TreeNode | null {
if (!node) {
return null
}
parentValues.push(node.value)
if (value.includes(node.value)) {
return node
}

return getCheckedParent(node.parent)
}

const checkedParent = getCheckedParent(root)
if (!checkedParent) {
return value
}

// 遞歸遍歷所有子節點
function dfs(node: TreeNode) {
if (!node.children || node.value === root.value) {
return
}
node.children.forEach((item: TreeNode) => {
if (item.value !== root.value) {
if (parentValues.includes(item.value)) {
dfs(item)
} else {
subTreeValues.push(item.value)
}
}
})
}
dfs(checkedParent)

// 替換 checkedParent 下子樹的值
const nextValue = removeAllDescendanceValue(checkedParent, value).filter(
(item) => item !== checkedParent.value
)
return Array.from(new Set(nextValue.concat(subTreeValues)))
}

Connected Checkbox

前面提到了我們只保存 Show_Parent 的值,部分選中的狀態和子節點選中的狀態是在運行時計算出來的。計算規則如下:

某個子節點是否為 checked 狀態 ⇒ 自己或本身的被 checked

某個父節點是否為 indeterminate 狀態 ⇒ 非 checked 狀態,並且有部分子節點被 checked

某個子節點是否為 unchecked 狀態 ⇒ 默認狀態

export const ConnectedCheckbox = React.memo(
(props: Pick<MenuItemProps, 'node'>) => {
const { node } = props
const { value: containerValue, handleSelectChange } = MultiCascader.useContainer()

const handleChange = useCallback(
(event: CheckboxChangeEvent) => {
const { checked } = event.target
handleSelectChange(node, checked)
},
[node]
)
// 自己或父節點為 checked
const checked = useMemo(() => hasParentChecked(node, containerValue), [
containerValue,
node,
])
// 自己沒有 checked,但是有子節點狀態 checked
const indeterminate = useMemo(
() => !checked && hasChildChecked(node, containerValue),
[checked, containerValue, node]
)

return (
<Checkbox
onChange={handleChange}
checked={checked}
indeterminate={indeterminate}
/>
)
}
)

hasParentChecked 和 hasChildChecked 方法就是簡單的向上或向下遍歷,代碼比較簡單這裡就省略了。

為了方便組件內狀態的管理(代碼中的 MultiCascader.useContainer),我引入了 unstated-next 這個庫,背後還是使用 Hooks + React Context,不過代碼更簡潔,Typescript 支持友好。

全選功能

有了以上的級聯 Checkbox 聯動之後,在繼續實現【全選】的邏輯也變得非常簡單。只需在原來的 data 至上再增加一個 All 節點,將改節點和 Footer 上的 All Checkbox 綁定即可。當第一級的所有節點都被選中之後,會自動沿 parent 向上提升 value。

const flattenData = useMemo(() => {
// 如果需要支持全選,在原來的 data 之上添加一個 TreeNode 節點
if (selectAll) {
return flattenTree([
{
title: 'All',
value: All,
parent: null,
children: data,
},
])
}
return flattenTree(data || [])
}, [data, selectAll])

// 如果需要支持全選,在 Footer 渲染 ConnectedCheckbox,賦予 All 節點
{selectAll ? (
<div className={`${prefix}-popup-all`}>
<ConnectedCheckbox node={flattenData[0]} />
&nbsp;&nbsp;{selectAllText}
</div>
) : null}

級聯菜單

默認情況展開菜單,需要列出所有第一級的父節點。如果支持全選,直接取第一個 flattenData 的 children,否則遍歷一遍 flattenData ,找到所有沒有 parent 的 children。

const [menuData, setMenuData] = useState([
selectAll
? flattenData[0].children!
: flattenData.filter((item) => !item.parent),
])

由於每個非葉子節點都保存了 children 的數據,級聯菜單其實就是在父節點被點擊之後,將其 children 添加到維護級聯狀態的數組中。

const addMenu = useCallback((menu: TreeNode[], index: number) => {
if (menu && menu.length) {
setMenuData((prevMenuData) => [...prevMenuData.slice(0, index), menu])
} else {
// 如果 children 也要更新 menu
// 比如當前展開了三級菜單,點擊了另一個二級葉子節點
setMenuData((prevMenuData) => [...prevMenuData.slice(0, index)])
}
}, [])

到這裡,整個級聯多選的核心邏輯已經開發完畢,剩下的一些細碎的邏輯不在這裡展開,感興趣的同學可以 github 上看源碼。

下一個部分介紹如何將用 Typescript 開發的 React 組件發布到 NPM。

Typescript NPM Package

我們的組件的代碼是用 Typescript 編寫的,然而用戶不一定使用 Typescript,也不一定會編譯 node_modules 下的文件,所以發布到 NPM 上的代碼應該是 JS 的形式。

Typescript 項目,首先需要安裝 typescript 和 tslib 兩個包。

$ yarn add typescript tslib -D

修改 package.json,添加 tsc script 和 main 欄位。

main 欄位是指定當別人使用這個包時的入口文件。原來的 Typescript 代碼入口是在 src/index.tsx 。dev 裡執行了 link 操作,這樣可以在本地項目中先進行驗證。tsc watch 可以在每次代碼變更時立馬重新編譯。 prepublishOnly 的作用是每次準備發布前重新編譯 Typescript。

"main": "dist/index.js",
"scripts": {
"dev": "yarn link && yarn tsc --watch",
"tsc": "tsc",
"prepublishOnly": "rm -rf dist/ && npm run tsc"
},

在根目錄下添加 tsconfig.json 文件,指定輸出路徑,是否生成聲明文件,執行 jsx 等等。declaration 欄位設為 true,在編譯時,Typescript 會自動生成 d.ts 文件。提供這些聲明文件,在 VSCode 等編輯器中就可以有代碼聯想提示的功能。

{
"compilerOptions": {
//...
"outDir": "dist",
// ...
"declaration": true,
// ...
"jsx": "react"
},
"include": ["./src/**/*"]
}

Less 文件怎麼辦?

組件中的樣式文件使用了 less 來編寫,同樣的不能要求使用這個包的用戶也必須用 less,我們需要提供一份 css 文件(Typescript 是沒辦法處理 less 文件的)。

// 在 less 文件中引入 antd 自帶的文件,以便使用其提供的變量
@import '../node_modules/antd/es/style/themes/default.less';

@prefix: ~'antd-multi-cascader';

.@{prefix} {
text-align: left;

&-hidden {
display: none;
}

//...
}

安裝 less

$ yarn add -D less

回到 package.json 文件,添加 lessc script,指定將 src/index.less 編譯到 dist/index.less。 —js 參數是因為引用了 antd 的 dfault.less 文件,裡面會用到了 inline javascript 的功能。

"scripts": {
"compile": "yarn tsc && yarn lessc",
"tsc": "tsc",
"lessc": "lessc src/index.less dist/index.css --js",
"prepublishOnly": "yarn test && rm -rf dist/ && npm run compile"
},

發布到 npm 之後,就可以在項目中應用組件和樣式文件了。✨

import MultiCascader from "antd-multi-cascader";
import "antd-multi-cascader/dist/index.css";

單元測試和 Github Actions

單元測試是成本較低卻能有效保障代碼質量的方法,除了能幫你找出代碼實際運行和預期不符的問題,還是排查問題的好工具。一般而言,單元測試覆蓋率越高,更容易獲得其他開發者的認可。

在前端領域,我們可以為方法,模塊,甚至一個組件編寫單元測試。編寫和運行單元測試首先需要安裝單元測試框架,以 facebook 的 jest 為例。

yarn add -D jest @types/jest ts-jest

項目根目錄下添加 jest.config.js 文件,讓 jest 知道如何解析,匹配哪些單元測試文件。

module.exports = {
preset: 'ts-jest',
testMatch: ['<rootDir>/src/**/__tests__/*.tsx'],
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
}

然後就可以編寫單元測試代碼了。全局的 describe 方法聲明一個單元測試分組,在這個組內可以定義單元測試生命周期的鉤子,before, after, beforeEach, afterEach 等等,在這些鉤子中可以為跑單元測試造數據。

it 方法則是具體某個用例執行的地方,會用到 jest 提供的 expect 方法,這個方法接受一個參數,然後返回一個具有很多斷言方法的對象。調用這些斷言方法即可起到驗證預期執行的效果。比如下面的例子中斷言 hasChildChecked(flattenValue[0], ['1']) 的返回值將為 true。給定輸入,驗證方法允許結果,如果下次不小心改壞了這個方法,單元測試就可以立即告訴你,及時修復避免雪球越滾越大。

import { hasChildChecked } from '..'

describe('src/components/MultiCascader/utils.tsx', () => {
describe('hasChildChecked', () => {
let flattenValue: TreeNode[]

beforeEach(() => {
flattenValue = createFlattenTree()
})

it('should tell has child checked or not', () => {
expect(hasChildChecked(flattenValue[0], ['1'])).toEqual(true)
})
})
})

編寫完單元測試,在 package.json scripts 中添加 test 命令,同時在 prepublishOnly 前也加上 test 任務,這樣每次打包前都能跑一遍單元測試。

"scripts": {
"test": "jest",
"prepublishOnly": "yarn test && rm -rf dist/ && npm run compile"
},

在持續集成的過程中,我們可以使用 Github Actions,Gitlab CI,Travis CI 等工具來跑單元測試、lint,sonarqube 等任務,保證代碼提交質量。

使用 Github Actions 很簡單,只需要在項目根目錄創建 .github/workflows/test.yml 文件。複製一下代碼,然後在每次提交代碼和 pr 時都會啟動一個 node 服務來運行單元測試了。

name: Test

on: [push, pull_request]

jobs:
release:
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [macos-10.14]

steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install
- run: npm run test -- --coverage

想要獲得展示 Coverage 的 Badge?,登錄 https://codecov.io/gh,選擇對應倉庫,將頁面上展示的 CODECOV_TOKEN 填到 Github 項目 Settings-Secrets 頁面上,然後把以下的代碼補充到 test.yml 文件中。

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true

然後,就可以通過 https://img.shields.io/codecov/c/github/[user]/[project]/master.svg 連結拿到 badge 啦。🎉

結語

相信閱讀完本文之後,你也可以動手開發一個級聯多選選擇器了。如果閱讀過程中有疑問歡迎留言討論。

作為滿足產品需求的最小實現,本文內容僅供參考,在實際項目中使用還需慎重,因為至少還存在以下缺陷。

數據量大時的性能要求,筆者在項目中的數據量非常小,編碼的時候沒有過多考慮性能問題。數據量很大的時候需要上 Virtual List

不支持搜索,動態加載菜單內容,不支持自定義 render

沒有經過嚴格測試,使用過程中參數變化可能會帶來預期之外的結果

沒有支持按鍵操作,Web 無障礙等

關於Checkbox 🌲 狀態聯動部分的實現,參考另一個非常棒的開源組件庫 rsuitejs - multi-cascader 的源碼。由於樣式、生態以及為了靈活應對項目需求,最終沒有選擇它,而是自行實現。

最後,所有代碼都在 github 上可以找到,如果你想了解更多細節可以點過去👀 歡迎 star

有同樣需求的同學,也歡迎試用,使用過程中有問題也歡迎提 issue~

// npm
$ npm install antd-multi-cascader
// yarn
$ yarn add antd-multi-cascader

連結

https://github.com/react-component/trigger

https://github.com/ant-design/ant-design/blob/master/components/cascader/index.tsx

https://rsuitejs.com/components/multi-cascader

1.看到這裡了就點個在看支持下吧,你的「點讚,在看是我創作的動力。

2.關注公眾號程式設計師成長指北,回復「1」加入高級前端交流群!「在這裡有好多 前端 開發者,會討論 前端 Node 知識,互相學習」!

3.也可添加微信【ikoala520】,一起成長。

「在看轉發」是最大的支持

相關焦點

  • Ant Design Mobile 2.2.0 發布,AntD 移動端設計規範
    Ant Design Mobile 2.2.0 已發布,更新內容如下:FeatureBug FixEnhancement注意:根據之前的 2.1.x 最後版本計劃,react native 組件代碼已分離到單獨的
  • vue-antd-admin 0.7.1 已經發布
    vue-antd-admin 0.7.1 已經發布。
  • 精通react/vue組件設計之實現一個Tag(標籤)和Empty(空狀態)組件
    "辛勤勞動",而是要根據已有前端的開發經驗,總結出一套自己的高效開發的方法.作為數據驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘代碼, 一切皆組件的思想深得人心.所以, 為了讓工程師們有更多的時間去考慮業務和產品迭代,我們不得不掌握高質量組件設計的思路和方法.所以筆者將花時間去總結各種業務場景下的組件的設計思路和方法,並用原生框架的語法去實現各種常用組件的開發,希望等讓前端新手或者有一定工作經驗的朋友能有所收穫
  • 基於jsoneditor二次封裝一個可實時預覽的json編輯器組件react版
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言做為一名前端開發人員,掌握vue/react/angular等框架已經是必不可少的技能了,我們都知道,vue或react等MVVM框架提倡組件化開發
  • 【Antd-Vue】你的cascader或select清空功能(allowClear)失效了嗎?
    然後找到了這樣的內容:antd Select組件的allowClear點擊失效 無法清空?觀察發現,由於同時設置了value屬性和allowClear屬性, 導致無法點擊X清空當前選中項,官方相應的文檔並沒有提供allowClear點擊事件讓我們訂製自己的事件,因此,解決辦法是去除value或者 獲取到X清空的dom元素,增加點擊事件.首先是一個失敗的問題復現在問題復現時發現貌似這個問題在新版本中已經得以解決了。
  • 乾貨貼:Web 後臺設計易被忽視的組件技巧
    說白了就是四個字:增、刪、改、查,如何更加優雅的錄入信息,讓整個表單填寫過程流暢、可控的進行是一個優秀的後臺產品經理必須掌握的技能。單選多選單選/多選按鈕的使用技巧:均不宜選項過多當一個問題的選項超過 5 個時,最好的選擇是使用選擇器另外單選/複選按鈕還需要考慮其禁用、可用狀態下不同的按鈕展現樣式
  • Diboot 2.0.5 發布,自動化開發助理
    Diboot 2.0.5 發布,自動化開發助理,為您賦能提效一、前言Diboot 2.0.5
  • 精通react/vue組件設計之實現一個輕量級可擴展的模態框組件
    之所以會寫組件設計相關的文章,是因為作為一名前端優秀的前端工程師,面對各種繁瑣而重複的工作,我們不應該按部就班的去"辛勤勞動",而是要根據已有前端的開發經驗,總結出一套自己的高效開發的方法.正文在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先來解構一下Modal組件, 一個Modal分為以下幾個部分:編輯搜圖每一個區塊都可以自定義配置
  • ng-zorro-antd 10.0.0 發布,使用 Angular 10 構建
    ng-zorro-antd 10.0.0 發布了。
  • 如何搭積木式的快速開發H5頁面?
    設計初衷筆者最開始開發這個項目的主要目的是提高個人和企業開發 H5 頁面的成本和效率, 可以通過搭積木的方式, 利用已有組件庫或外部組件資源(正在設計)搭建出適合不同場景的 H5 應用, 並且支持一鍵下載代碼, 讓技術人員輕鬆將H5頁面部署到自己的伺服器中.
  • 《精通react/vue組件設計》之實現一個健壯的警告提示(Alert)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第七篇文章, 今天帶大家實現一個自帶主題且可關閉的Alert組件, 該組件在諸如Antd或者elementUI等第三方組件庫中都會出現,主要用來提供系統的用戶反饋
  • 精通react/vue組件設計教你實現一個極具創意的加載(Loading)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第八篇文章, 今天帶大家用5分鐘實現一個極具創意的加載(loading)組件.涉及的核心知識點主要是css3相關特性, 如果大家非常熟悉,可直接跳過介紹直接看正文.時刻問自己:是否具備創造力?
  • 詳解FPGA如何實現FP16格式點積級聯運算
    編者按:通過使用Achronix Speedster7t FPGA中的機器學習加速器MLP72,開發人員可以輕鬆選擇浮點/定點格式和多種位寬,或快速應用塊浮點,並通過內部級聯可以達到理想性能。
  • 精通React/Vue系列之帶你實現一個功能強大的通知提醒框
    其他業務類型熟悉以上分類法是設計任何組件系統的前提,不管你是從零到一開發前端團隊的UI庫,還是基於已有組件庫二次開發業務組件,以上分類法則同樣適用。正文在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先來解構一下Notification組件, 一個Notification分為以下幾個部分:每一個區塊都可以自定義配置, 也可以組合其他組件.並且我們可以配置提醒框出現的位置,就像antd
  • 多交換機3大核心技術:級聯、堆疊、集群
    在多交換機的區域網環境中,交換機的級聯、堆疊和集群是3種重要的技術。級聯技術可以實現多臺交換機之間的互連;堆疊技術可以將多臺交換機組成一個單元,從而提高更大的埠密度和更高的性能;集群技術可以將相互連接的多臺交換機作為一個邏輯設備進行管理,從而大大降低了網絡管理成本,簡化管理操作。
  • React中的樣式化:從外部CSS到樣式化組件
    React中的樣式化:從外部CSS到樣式化組件,要獲得一個高質量、深入的介紹,您不能超越加拿大的全棧開發人員Wes Bos。在這裡嘗試他的課程,並使用代碼SITEPOINT獲得25%的折扣,並幫助支持SITEPOINT。
  • 淘寶開放平臺開發指南之架構組件體系介紹
    圖中,紅色虛線就是TOP的Skeleton。TOP當前從業務模塊功能角度來劃分,可以分成三個層次:基礎平臺組件層,基礎業務組件層,普通業務組件層。頻率控制及流量控制除了保護TOP自身不受到攻擊,也為後端服務提供者作了天然的一個保護屏障,保證服務請求壓力可以在TOP上可控,防止流量直接壓倒服務提供者。用戶隱私安全在淘寶尤為重要,用戶信息的安全性也在淘寶開放的過程中被放到了首位。在開放平臺設計中,除了採用普通開放平臺的認證模式以外(OAuth類似流程),還在服務調用過程中通過區分應用角色來限制對於用戶信息的獲取和使用。
  • 安卓開發四大組件
    Android應用程式由一些零散的有聯繫的組件組成,通過一個工程manifest綁定在一起。在manifest中,描述了每一個組件以及組件的作用,其中有6個組件,它們是Android應用程式的基石。Android有四大組件(也有說六大組件的,外加Intent和Notification),分別是Activity,Service,Content Provider和BroadcastReceiver。這四大組件一起組成了完整的Android程序。我們將分別簡要介紹。
  • 優化選項較多的多選下拉列表的可用性
    01—現有的UI解決方案當你查找多選的UI解決方案的時候,通常會找到類似的解決方案:給出一個既可以滾動又可以搜索的下拉菜單,並在輸入欄位中像藥片一樣展示選項。例如,混合雙列表與doejo的過濾解決方案我們的系統已經有一個Select2組件,與第一個例子相類似。
  • 產品和過程FMEA
    情形1:新設計、新技術或新過程完整的設計、技術或過程。情形2:現有設計或過程的新應用新環境、新場地或新應用對現有設計或過程的影響。情形3:対現有設計或過程的工程變更新技術開發、新要求、產品召回和使用現場失效可能會需要變更設計和/或過程。