React實現checkbox group多組選項和標簽組顯示的聯動


實現功能:勾選checkbox項,確定后,已勾選的checkbox項以tag標簽的形式展示,tag標簽可快捷刪除。

checkbox group
tags

實現過程:

  • 使用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可能會有報錯。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM