1、使用ag-Grid做数据表格非常方便,但是很多方法都是enterprise版本,即企业级付费才能使用的功能,例如Tool Panel,下面讲一下自定义Tool Panel来控制column的显示与隐藏功能
1.1、ag-Grid的tool panel具体效果图为
tool panel-摘自ag-Grid我们自定义无非是创建一个div根据aggrid的表格数据生成一个树形结构,在用这个树形结构的按钮控制column的显示与隐藏,树形结构我们可以使用antd的tree,话不多说上代码
1.2、先使用ant的sider,创建一个customersider来包装tree
<CustomSider
width={0}
className="CustomSider"
collapsed={collapsed} //外部开关,控制customerside的呼出与退出
defaultCollapsed
collapsedWidth={320}
checkedKeyArr={initCheckedks} //初始化选中的节点
columnDefs={this.state.columnDefs} //数据源即aggrid的数据源
downLoadColumnDefs={defaultGridConfigure && defaultGridConfigure.layout && defaultGridConfigure.layout.customerColumnDefs && defaultGridConfigure.layout.customerColumnDefs.length ? defaultGridConfigure.layout.customerColumnDefs : this.state.columnDefs} //用作上传下载的数据源
updateColumns={(newColumns, columnVisibleUpdate) => this.updateColumns(newColumns, columnVisibleUpdate)} //回调方法,更新用户设置,返回一个新的columnDefs内包含hide属性的true or false
/>
2.1 customerSider.js
import React from 'react';
import { Layout, Tree, Input, Checkbox } from 'antd';
import './CustomSider.css';
import '../../Common/base.css';
const TreeNode = Tree.TreeNode;
const { Sider } = Layout;
export default class CustomSider extends React.Component {
constructor(props) {
super(props);
this.defaultCheckedAllKeys = this.initAllCheckedArr();
const { checkedKeyArr } = this.props;
this.state = {
expandedKeys: [],
autoExpandParent: true,
checkedKeys: checkedKeyArr,
selectedKeys: [],
defaultExpandAll: true,
isSearched: false,
indeterminate: true,
checkAll: false,
keys: checkedKeyArr,
};
this.dataList = [];
}
arrContentObject(arr, obj) {
let contantObj = false;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == obj) {
contantObj = true;
}
}
return contantObj;
}
arrColumnIdContentObject(arr, obj) {
let contantObj = false;
for (let i = 0; i < arr.length; i++) {
if (arr[i].columnId == obj) {
contantObj = true;
}
}
return contantObj;
}
getRootNodeKey(item, key) {
const { columnDefs } = this.props;
let tempItem = columnDefs[1];
columnDefs.map((item) => {
if (item.children) {
if (this.arrColumnIdContentObject(item.children, key)) {
tempItem = item;
}
}
});
return tempItem;
}
arrContentOtherAllArrObject(keyArr, otherArr) {
let contantAll = true;
for (let i = 0; i < otherArr.length; i++) {
if (!this.arrContentObject(keyArr, otherArr[i].columnId)) {
contantAll = false;
}
}
return contantAll;
}
arrContentOtherArrObject(keyArr, otherArr) {
let isContant = false;
keyArr.map((item) => {
for (let i = 0; i < otherArr.length; i++) {
if (item == otherArr.columnId) {
isContant = true;
return isContant;
}
}
});
return isContant;
}
indexOfObjInArr(arr, obj) {
let index = 0;
for (let i = 0; i < arr.length; i++) {
if (obj == arr[i]) {
index = i;
return index;
}
}
return index;
}
componentDidMount() {
this.generateList(this.props.columnDefs);
}
onExpand(expandedKeys) {
this.setState({
expandedKeys,
autoExpandParent: false,
});
}
getCheckedKeys() {
const { downLoadColumnDefs } = this.props;
const initCheckedks = [];
downLoadColumnDefs.map((item) => {
if (item.children) {
for (let i = 0; i < item.children.length; i++) {
if (!item.children[i].hide) {
initCheckedks.push(item.children[i].columnId);
}
}
if (this.arrleafAllShow(item.children)) {
initCheckedks.push(item.columnId);
}
}
});
return initCheckedks;
}
handleCheckedKeysArr(e) {
// const { columnDefs } = this.props;
const { downLoadColumnDefs } = this.props;
const { keys } = this.state;
for (let i = 1; i < downLoadColumnDefs.length; i++) {
if (downLoadColumnDefs[i].columnId == e.node.props.eventKey) {
const item = downLoadColumnDefs[i];
if (e.checked) {
if (this.arrContentObject(keys, item.columnId)) {
} else {
keys.push(item.columnId);
}
for (let i = 0; i < item.children.length; i++) {
if (this.arrContentObject(keys, item.children[i].columnId)) {
} else {
keys.push(item.children[i].columnId);
}
}
} else {
if (this.arrContentObject(keys, item.columnId)) {
const itemIndex = this.indexOfObjInArr(keys, item.columnId);
keys.splice(itemIndex, 1);
}
for (let i = 0; i < item.children.length; i++) {
if (this.arrContentObject(keys, item.children[i].columnId)) {
const itemIndex = this.indexOfObjInArr(keys, item.children[i].columnId);
keys.splice(itemIndex, 1);
}
}
}
} else { // leaf
const rootItem = this.getRootNodeKey(downLoadColumnDefs[i], e.node.props.eventKey);
if (e.checked) { // checked = true
if (this.arrContentObject(keys, e.node.props.eventKey)) {
} else {
keys.push(e.node.props.eventKey);
}
if (this.arrContentOtherAllArrObject(keys, rootItem.children)) { // all check
if (this.arrContentObject(keys, rootItem.columnId)) { // contant root node
} else {
keys.push(rootItem.columnId);
}
}
} else { // checked = fasle
if (this.arrContentObject(keys, e.node.props.eventKey)) {
const index = this.indexOfObjInArr(keys, e.node.props.eventKey);
keys.splice(index, 1);
}
if (this.arrContentOtherArrObject(keys, rootItem.children)) {
} else if (this.arrContentObject(keys, rootItem.columnId)) {
const itemIndex = this.indexOfObjInArr(keys, rootItem.columnId);
keys.splice(itemIndex, 1);
}
}
}
}
}
onCheck(checkedKeys, e) {
const { downLoadColumnDefs } = this.props;
this.handleCheckedKeysArr(e);
const { keys } = this.state;
this.setState({ checkedKeys: keys });
const customerColumnDefs = this.matchColumnsKey(downLoadColumnDefs, e.node.props.eventKey, e.checked);
this.handleFilterCheckState(keys.length);
if (!this.state.checkAll && !this.state.indeterminate) {
customerColumnDefs[0].hide = true;
} else {
customerColumnDefs[0].hide = false;
}
this.props.updateColumns(customerColumnDefs, true);
}
getParentKey(key, tree) {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
node.children.forEach((element) => {
if (element.columnId === key) {
element.showTreeNode = true;
}
});
if (node.children.some(item => item.columnId === key)) {
node.showTreeNode = true;
parentKey = node.columnId;
} else if (this.getParentKey(key, node.children)) {
parentKey = this.getParentKey(key, node.children);
}
if (!!parentKey && node.columnId === key) {
node.showTreeNode = true;
}
}
}
return parentKey || key;
}
matchColumnsKeyAll(downLoadColumnDefs, key, checked) {
downLoadColumnDefs.map((item) => {
if (item.children) {
for (let i = 0; i < item.children.length; i++) {
item.children[i].hide = !checked;
}
} else if (item.columnId === key) {
item.hide = !checked;
}
});
return [].concat(downLoadColumnDefs);
}
matchColumnsKey(downLoadColumnDefs, key, checked) {
downLoadColumnDefs.map((item) => {
if (item.children) {
if (item.columnId == key) {
for (let i = 0; i < item.children.length; i++) {
item.children[i].hide = !checked;
}
} else {
this.matchColumnsKey(item.children, key, checked);
}
} else if (item.columnId == key) {
item.hide = !checked;
}
});
return [].concat(downLoadColumnDefs);
}
generateList(data) {
for (let i = 0; i < data.length; i++) {
const node = data[i];
this.dataList.push({ columnId: node.columnId, headerName: node.headerName });
if (node.children) {
this.generateList(node.children);
}
}
}
handleFilterCheckState(checkedLenth) {
if (checkedLenth === this.defaultCheckedAllKeys.length) {
this.state.checkAll = true;
this.state.indeterminate = false;
} else if (checkedLenth === 0) {
this.state.checkAll = false;
this.state.indeterminate = false;
} else {
this.state.checkAll = false;
this.state.indeterminate = true;
}
}
initAllCheckedArr() {
const { columnDefs } = this.props;
const initCheckedks = [];
columnDefs.map((item) => {
if (item.children) {
initCheckedks.push(item.columnId);
for (let i = 0; i < item.children.length; i++) {
initCheckedks.push(item.children[i].columnId);
}
}
});
return initCheckedks;
}
searchTreeNodes(value) {
const { columnDefs } = this.props;
columnDefs.map((item) => {
if (item.children) {
item.showTreeNode = false;
item.children.map((childrenItem) => {
childrenItem.showTreeNode = false;
});
} else {
item.showTreeNode = false;
}
});
const expandedKeys = this.dataList.map((item) => {
if (item.headerName && item.headerName.toLowerCase().indexOf(value.toLowerCase()) > -1) {
return this.getParentKey(item.columnId, columnDefs);
}
return null;
}).filter((item, i, self) => item && self.indexOf(item) === i);
this.setState({
expandedKeys,
searchValue: value,
autoExpandParent: true,
isSearched: true,
});
}
selectAllNodes(taget) {
let tempCheckedIdArr = [];
let newCol = [];
const { downLoadColumnDefs } = this.props;
if (taget.checked) { // all checked
this.state.indeterminate = false;
this.state.checkAll = true;
tempCheckedIdArr = this.initAllCheckedArr();
newCol = this.matchColumnsKeyAll(downLoadColumnDefs, 0, true);
} else {
this.state.checkAll = false;
tempCheckedIdArr = [];
newCol = this.matchColumnsKeyAll(downLoadColumnDefs, 0, false);
}
this.setState({ checkAll: this.state.checkAll, keys: tempCheckedIdArr, checkedKeys: tempCheckedIdArr });
this.props.updateColumns(newCol, true);
}
renderTreeNodes(data, isSearched) {
if (isSearched) {
return data.map((item) => {
if (item.headerName && item.showTreeNode) {
if (item.children) {
return (
<TreeNode title={item.headerName} key={item.columnId} dataRef={item}>
{this.renderTreeNodes(item.children, isSearched)}
</TreeNode>
);
}
return (<TreeNode title={item.headerName} key={item.columnId} />);
}
return <span />;
});
}
return data.map((item) => {
if (item.headerName) {
if (item.children) {
return (
<TreeNode title={item.headerName} key={item.columnId} dataRef={item}>
{this.renderTreeNodes(item.children, isSearched)}
</TreeNode>
);
}
return (<TreeNode title={item.headerName} key={item.columnId} />);
}
return <span />;
});
}
render() {
const { columnDefs } = this.props;
const { isSearched } = this.state;
const { checkedKeyArr } = this.props;
this.handleFilterCheckState(checkedKeyArr.length);
return (
<div>
<Layout>
<Sider
{...this.props}
>
<h3 className="siderTitle">Show/Hide Columns</h3>
<div className="treeSearhWrap">
<Checkbox
className="selectAllNodes"
indeterminate={this.state.indeterminate}
checked={this.state.checkAll}
onChange={
(e) => {
this.selectAllNodes(e.target);
}
}
/>
<Input
placeholder="Search..."
size="small"
className="searchNodes"
onChange={e => this.searchTreeNodes(e.target.value)}
/>
</div>
<Tree
className="columnTree"
checkable
onExpand={param => this.onExpand(param)}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={(param, e) => this.onCheck(param, e)}
// checkedKeys={this.state.checkedKeys}
checkedKeys={checkedKeyArr}
// checkedKeys={this.state.defaultCheckedKey}
keys={this.state.keys}
onSelect={(selectParam) => { this.onSelect(selectParam); }}
selectedKeys={this.state.selectedKeys}
defaultExpandAll={this.state.defaultExpandAll}
>
{this.renderTreeNodes(columnDefs, isSearched)}
</Tree>
</Sider>
</Layout>
</div>
);
}
}
2.1.1 antd的tree给一个树形控件的数据源就可以自己管理checkedkeys,但是由于我们需要做搜索功能,如图,
完整数据源渲染的tree 搜索后的数据源渲染的tree这里搜索的时候只能通过return <span />;那么return span的时候我们之前的checkedkeys,就会被改变而导致错误,所以我们只能自己维护checkedkeys,也就有了
handleCheckedKeysArr
方法,作为一个ios开发写前端确实有很多习惯上的不同,但是毕竟我以前了解过前端,虽然上述代码写的不是很好,但是功能没毛病,后期会在优化的,等着
3.1、以上为tool panel自定义,具体的隐藏和显示我们要从aggrid中寻找方法,有一个columnApi.setColumnVisible('filedId', true);
,那么问题来了,如何触发这个方法呢,我们可以在aggrid中添加监听事件
onComponentStateChanged={(e) => {
const { columnVisibleUpdate, downLoadColumnDefs } = this.props;
if (columnVisibleUpdate) {
this.handleColumnDefsVisibleChanged(downLoadColumnDefs);
}
}}
注意这个onComponentStateChanged
这个方法会在很多时候调用,比如更新pin,sort等等,所以我们设置show or hide的时候要加个bool做判断,当aggrid的columnDefs发生改变的时候回调用此方法,然后我们根据用户设置的columnDefs来控制column显示和隐藏
handleColumnDefsVisibleChanged(columnDefs) {
columnDefs.map((item) => {
if (item.children) {
for (let i = 0; i < item.children.length; i++) {
this.columnApi.setColumnVisible(item.children[i].field, !item.children[i].hide);
}
} else {
this.columnApi.setColumnVisible('lockPosition', !item.hide);
}
});
}
lockPosition为我们的状态显示栏,当所有的column隐藏的时候,我们才隐藏lockPosition,至此,所有的column 显示和隐藏操作完成
网友评论