React实现checkbox group多组选项和标签组显示的联动
实现功能:勾选checkbox项,确定后,已勾选的checkbox项以tag标签的形式展示,tag标签可快捷删除。
实现过程:
- 使用React。
- 使用Ant Design的Checkbox、Tag组件。
- 整个组件主要分为两个部分:多选框组和Tag标签组。
1. 多选框组
class AddInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedList: [], // checkbox已选择的选项
indeterminate: [], // 全选框-已有选择非全选
checkAll: {}, // checkbox group 的全部title状态true/false
tempList: [], // 临时存储checkbox已选择的选项
checkTitle: {} // checkbox group中已选择的title(全选)
};
}
/* 确定勾选 */
handleOk = () => {
if (this.state.tempList.length > 0) {
// 将已选择信息传给Tags组件
this.props.getChecked({
checkedItem: this.state.tempList,
checkAll: this.state.checkAll,
indeterminate: this.state.indeterminate,
checkTitle: this.state.checkTitle
});
}
}
/* checkbox单选 */
onChange = (allCheckArr, checkedList) => {
let checkAll = this.state.checkAll;
let indeterminate = [];
let checkTitle = {};
Object.keys(allCheckArr).forEach((title) => {
checkTitle[title] = 0;
for (let checkedItem of checkedList || []) {
if (allCheckArr[title].includes(checkedItem)) {
checkTitle[title]++;
checkAll[title] = checkTitle[title] === allCheckArr[title].length;
indeterminate[title] = !!checkTitle[title] && (checkTitle[title] < allCheckArr[title].length);
}
}
if (checkTitle[title] === 0) { // 选项组下仅有一个选项时取消选中
checkAll[title] = false;
}
});
this.setState({
checkedList,
tempList:checkedList,
indeterminate: indeterminate,
checkAll: checkAll,
checkTitle: checkTitle
});
}
/* checkbox全选 */
onCheckAllChange = (allCheckArr, title, e) => {
this.state.checkAll[title] = e.target.checked;
let checkedListT = [];
checkedListT.push(...this.state.checkedList);
let indeterminate = this.state.indeterminate || [];
let checkTitle = this.state.checkTitle || {};
if (e.target.checked === true) { // 全选
checkedListT.push(...allCheckArr[title]);
checkedListT = Array.from(new Set(checkedListT)); // 去重(原先indeterminate为true的情况)
checkTitle[title] = allCheckArr[title].length;
} else { // 取消全选
let common = checkedListT.filter(v => allCheckArr[title].includes(v));
checkedListT = checkedListT.concat(common).filter(v => checkedListT.includes(v) && !common.includes(v));
checkTitle[title] = 0;
}
indeterminate[title] = false;
this.setState({
tempList: checkedListT,
checkedList: checkedListT,
indeterminate: indeterminate,
checkTitle: checkTitle
});
}
render() {
const { checkedList, checkAll, indeterminate } = this.state;
const { allCheckArr } = this.props;
return (
<div className={styles.modalcontent} >
{
allCheckArr.map( ({ title, value }, key ) => (
<div className={styles.checksgroup}>
<div>
<Checkbox
indeterminate={indeterminate[title]}
onChange={this.onCheckAllChange.bind(this, allCheckArr, title)}
checked={checkAll[title]}
>
{title}
</Checkbox>
</div>
<br />
<CheckboxGroup className={styles.contents} options={value} value={checkedList} onChange={this.onChange.bind(this, allCheckArr)} />
</div>
))}
</div>
);
}
}
export default AddInfo;
- 由于Ant Design官网上checkbox group的示例代码只有一个check group,本组件是可以有多组的情况,因此主要通过checkedList,checkAll,indeterminate,checkTitle几个状态控制checkbox group与单个的checkbox的全勾选、半勾选、无勾选几种情况的联动。
- checkbox单选的操作是传入当前选择的所有的选项,然后与原先的可选项对比,计算出checkAll,indeterminate,checkTitle的值。每次要先将checkAll,indeterminate,checkTitle置空,遍历所有的已选项和待选项。
- checkbox全选的函数本来是可以复用单选的操作,但是全选之后得出checkAll,indeterminate,checkTitle的值的过程比单选更简单一些,不用遍历选项数组,所以重写了全选的逻辑,没有复用单选的函数,虽然代码量多几行,但是执行过程更简单一些。
2. Tag标签组
import React from 'react';
import { Tag } from 'antd';
import styles from './index.less';
class Tags extends React.Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items, // 需要显示的tag数组
checkAll: this.props.checkAll, // 该tag所在的数组元素是否全部作为tag存在
indeterminate: this.props.indeterminate, // 该tag所在的数组元素是否部分作为tag存在
allCheckArr: this.props.allCheckArr, // 该tag所在的数组
checkTitle: this.props.checkTitle // 该tag所在的数组元素作为tag存在的数量
};
}
componentWillReceiveProps = ( value, nextState) => {
this.setState({
items: value.items,
checkAll: value.checkAll,
indeterminate: value.indeterminate,
allCheckArr: value.allCheckArr,
checkTitle: value.checkTitle
});
}
delete = (key, value, e) => {
e.preventDefault();
let items = this.state.items;
let checkAll = this.state.checkAll;
let indeterminate = this.state.indeterminate;
let allCheckArr = this.state.allCheckArr;
let checkTitle = this.state.checkTitle;
items.splice(key, 1);
for (let title in allCheckArr) {
for (let item of allCheckArr[title]) {
if (item === value) {
checkTitle[title]--;
checkAll[title] = false;
if (checkTitle[title] === 0) { // 该选项组下的选项全部删除
indeterminate[title] = false;
} else {
indeterminate[title] = true;
}
}
}
}
this.setState({
items: items,
checkAll: checkAll,
indeterminate: indeterminate,
checkTitle: checkTitle
});
this.props.changeCheckeditems(items);
}
render() {
const items = this.state.items?this.state.items:[];
return (
<div>
{
items.map((value, key) => (
<Tag className={styles.singletag} closable key={key} onClose={this.delete.bind(this, key, value)}>{value}</Tag>
))}
</div>
);
}
}
export default Tags;
在多选框组对选项勾选之后,将选择结果传入Tags标签组件,该组件以Tag标签将已勾选的选项展示出来。Tag标签可以点击右边的“x”快捷删除,删除后,多选框组中相应的选项也会取消勾选。
3. 组件使用
这两个组件放在同一个父组件中使用,实现值传递。
class parent extends React.Component {
/* 获取待选择选项 */
getAllCheckArr = () => {
...
this.setState({
allCheckArr
...
});
...
}
/* 获取checkbox选择的选项 */
getChecked = (val) => {
this.setState({
checkedItem: val.checkedItem,
checkAll: val.checkAll,
indeterminate: val.indeterminate,
checkTitle: val.checkTitle
});
}
/* 获取tags删除后的选项 */
changeChecked = (val) => {
...
this.setState({
changedItem: val
});
...
}
render() {
const { checkedItem, changedItem,, checkAll, indeterminate, checkTitle } = this.state;
return (
...
<AddInfo
checkList={this.state.checkedItem}
allCheckArr={this.state.allCheckArr|| []}
getChecked={this.getChecked.bind(this)}
/>
<Tags
allCheckArr={this.state.allCheckArr|| []}
checkAll={checkAll}
checkTitle={checkTitle}
indeterminate={indeterminate}
items={checkedItem}
changeChecked={this.changeChecked.bind(this)}
/>
...
);
}
}
代码经过了比较大程度的删改,删除了许多无关功能,只保留了组件功能的核心部分,因此直接copy可能会有报错。。。