AgGrid树网格在同一组级别上未显示相同的叶子名称项

时间:2018-10-05 13:33:11

标签: ag-grid

我正在将AgGrid与树数据一起使用。 https://www.ag-grid.com/javascript-grid-tree-data/

问题是我需要在树上有两个具有相同名称的叶节点,因为它们共享名称,但不共享其属性。 我认为这是由getRowNodeId GridOption指定的。选项1:https://www.ag-grid.com/javascript-grid-rxjs/中的用法示例。 但事实并非如此。

他是我该财产的代码:

 ...
 getRowNodeId: (data: any) => any = (data) => {
    return (data.parent !== undefined ? data.parent * 1000 : 0) + data.properties.id;
 },
 ...

Screenshot of the grid

如您所见,我想添加2个具有相同名称但只有1个渲染器的节点。我该怎么办?

更新: 已添加代码

我的AgGrid组件:

import { Component, ViewChild, ElementRef, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
import { Events, ToastController, AlertController } from 'ionic-angular';
import * as _ from 'lodash';
import 'ag-grid-enterprise';
import { Http } from '@angular/http';
import { GridApi, TemplateService, GridOptions, ColDef } from 'ag-grid';
import { TemplateServiceProvider } from '../../providers/services';
import { NgModel } from '@angular/forms';
import { Toast } from '../../classes/classes';
import { EventSubscriber } from '../../classes/eventSubscriber/eventSubscriber';

@Component({
    selector: 'page-datatableWithGroups',
    templateUrl: 'datatableWithGroups.html'
})
export class DatatableWithGroupsPage extends EventSubscriber implements OnDestroy {

    @ViewChild('quickFilterInput') quickFilterInput: ElementRef;
    @Input() rowData: any[] = [];
    @Input() columnDefs = [];
    @Input() title: string;
    @Output() onLoadRow: EventEmitter<any> = new EventEmitter();
    @Output() onAddRow: EventEmitter<any> = new EventEmitter();
    @Output() onDeleteRow: EventEmitter<any> = new EventEmitter();
    @Output() onDuplicateRow: EventEmitter<any> = new EventEmitter();
    @Output() onSelectedRow: EventEmitter<any> = new EventEmitter();
    public gridApi: GridApi;
    public gridColumnApi;
    public printPending: boolean = true;
    public showColumnTools: boolean;
    public showColumnVisibilityTools: boolean;
    public showFilterTools: boolean = true;
    private groupDefaultExpanded;
    private hoverNode = null;
    private isControlPressed: boolean;
    private newGroupsCounter: number = 0;
    private autoGroupColumnDef: ColDef = {
        rowDrag: true,
        headerName: "Group",
        width: 250,
        suppressMovable: true,
        cellRendererParams: {
            suppressCount: true,
        },
        valueFormatter: (params) => {
            if (!params.data.properties.checkpointList) return params.value;
            return params.value + ' (' + (params.data.subGroups ? params.data.subGroups.length : 0) + ')'
                + '(' + (params.data.properties.checkpointList ? params.data.properties.checkpointList.length : 0) + ') ';
        },
        cellClassRules: {
            "hover-over": (params) => {
                return params.node === this.hoverNode;
            },
            "checkpointGroup-title": (params) => {
                return this.isCheckpointGroup(params.node.data);
            }
        }
    };

    addRowFunction: () => void;
    isCheckpointGroup: (nodeData) => boolean = (nodeData) => {
        return nodeData && nodeData.properties.checkpointList;
    }
    getDataPath: (data: any) => any = (data) => {
        return data.properties.orgHierarchy;
    };
    getRowNodeId: (data: any) => any = (data) => {
        return (data.properties.parent !== undefined ? data.properties.parent * 1000 : 0) + data.properties.id + (data.properties.component !== undefined ? data.properties.component.id * 100000 : 0);
    }
    getRowNodeWithUpdatedId: (data: any) => any = (data) => {
        return (data.properties.parentId !== undefined ? data.properties.parentId * 1000 : (data.properties.parent !== undefined ? data.properties.parent * 1000 : 0)) + data.properties.id + (data.properties.component !== undefined ? data.properties.component.id * 100000 : 0);
    }
    public gridOptions: GridOptions = {
        enableFilter: this.showFilterTools,
        enableColResize: true,
        animateRows: true,
        cacheQuickFilter: this.showFilterTools,
        treeData: true,
        quickFilterText: this.quickFilterInput ? this.quickFilterInput.nativeElement.value : '',
        colResizeDefault: 'shift',
        groupDefaultExpanded: this.groupDefaultExpanded,
        rowSelection: 'single',
        rowDeselection: true,
        defaultColDef: {
            filter: "agTextColumnFilter"
        },
        // components: this.components,
        getDataPath: this.getDataPath,
        getRowNodeId: this.getRowNodeId,
        autoGroupColumnDef: this.autoGroupColumnDef,
        deltaRowDataMode: true,
        onModelUpdated: () => {
            if (this.gridApi && this.columnDefs) {
                this.gridColumnApi.autoSizeColumn('ag-Grid-AutoColumn');
                this.adjustColumnsToFitWindow();
            }
        },
        onSelectionChanged: (event) => {
            let selectedRows = event.api.getSelectedNodes();
            let selectedRow = selectedRows && selectedRows.length > 0 ? selectedRows[0] : undefined;
            if (selectedRow && this.isCheckpointGroup(selectedRow.data)) {
                this.onSelectedRow.emit(selectedRow);
            } else {
                this.onSelectedRow.emit(undefined);
            }
        },
    };


    constructor(
        events: Events,
        public http: Http,
        public templateService: TemplateServiceProvider,
        public toastController: ToastController,
        public alertCtrl: AlertController,

    ) {
        super(events, [
            { eventName: 'datatable:updateList', callbackFunction: () => this.onLoadRow.emit() },
            { eventName: 'datatable:resizeTable', callbackFunction: () => this.gridApi.sizeColumnsToFit() }
        ])
        this.groupDefaultExpanded = -1;
    }

    ngOnInit() {
        super.subscribeEvents();
        this.subscribeEvents();
    }

    subscribeEvents() {
        this.addRowFunction = () => { this.onAddRow.emit() };
        window.onresize = () => this.adjustColumnsToFitWindow();
        document.addEventListener('keydown', (evt: any) => {
            evt = evt || window.event;
            if ((evt.keyCode && evt.keyCode === 17) || (evt.which && evt.which === 17)) this.isControlPressed = !this.isControlPressed;
        });
    }

    updateRowData(rowData) {
        this.gridApi.setRowData(rowData);
        this.gridApi.clearFocusedCell();
        this.reAssignSortProperty();
    }

    onGridReady(params) {
        this.gridApi = params.api;
        this.gridColumnApi = params.columnApi;
        this.adjustColumnsToFitWindow();
        if (!this.rowData || this.rowData.length === 0) this.gridApi.hideOverlay()
    }

    onRowDragMove(event) {
        this.setHoverNode(event);
    }

    onRowDragLeave() {
        this.setHoverNode(undefined);
    }

    setHoverNode(event) {
        let overNode = event ? event.overNode : undefined;
        if (this.hoverNode === overNode) return;
        var rowsToRefresh = [];
        if (this.hoverNode) {
            rowsToRefresh.push(this.hoverNode);
        }
        if (overNode) {
            rowsToRefresh.push(overNode);
        }
        this.hoverNode = overNode;
        this.refreshRows(rowsToRefresh);
    }

    refreshRows(rowsToRefresh) {
        var params = {
            rowNodes: rowsToRefresh,
            force: true
        };
        this.gridApi.refreshCells(params);
    }

    onRowDragEnd(event) {
        let toIndex;
        let overNode = event.overNode;
        let movingNode = event.node;
        let movingData = movingNode.data;
        if (overNode === movingNode) return;
        if (!overNode) {
            if (this.isCheckpointGroup(movingData)) {
                overNode = (<any>this.gridApi.getModel()).rootNode;
                toIndex = this.rowData.length;
            } else {
                return;
            }
        }
        let overData = overNode.data;
        let fromIndex = this.rowData.indexOf(movingData);
        toIndex = toIndex ? toIndex : this.rowData.indexOf(overData) + 1;


        let newParentNode = (!overNode.data || this.isCheckpointGroup(overNode.data)) ? overNode : overNode.parent;
        if (overData && (overData.properties.parentId === 0 || (!overData.properties.parentId && overData.properties.parent === 0)) && this.isCheckpointGroup(movingData) && this.isControlPressed) {
            overNode = (<any>this.gridApi.getModel()).rootNode;
            newParentNode = (!overNode.data || this.isCheckpointGroup(overNode.data)) ? overNode : overNode.parent;
        } else if (overData && this.isControlPressed) {
            newParentNode = overNode.parent;
        }
        if (toIndex > fromIndex) toIndex--;

        let oldParentNode = movingNode.parent;
        let newParentPath = (newParentNode.data && this.isCheckpointGroup(newParentNode.data)) ? newParentNode.data.properties.orgHierarchy : [];

        let needToChangeParent = !this.arePathsEqual(newParentPath, movingData.properties.orgHierarchy);

        if (fromIndex === toIndex && !needToChangeParent) return;

        if (this.isInvalidMoveTo(movingNode, newParentNode)) {
            new Toast(this.toastController).showToast('Invalid Move');
            return;
        }

        if (needToChangeParent) {

            if (this.checkNodeExistsInParent(movingData, newParentNode)) return;
            let updatedRows = [];
            this.moveToPath(newParentPath, movingNode, updatedRows, oldParentNode.data, newParentNode.data || { properties: { id: 0 } });
            this.gridApi.updateRowData({ update: updatedRows });
            this.refreshRows(this.rowData);
        }

        let newStore = this.rowData.slice();
        this.moveInArray(newStore, fromIndex, toIndex);
        this.gridApi.setRowData(newStore);
        this.rowData = newStore;
        this.setHoverNode(undefined);
    }

    checkNodeExistsInParent(newRow, newParentNode) {
        let cloneRow = _.cloneDeep(newRow);
        cloneRow.properties.parentId = newParentNode && newParentNode.data ? newParentNode.data.properties.id : 0;
        if (this.isCheckpointGroup(cloneRow) && this.existsInParent(cloneRow)) {
            new Toast(this.toastController).showToast('"' + cloneRow.properties.name + '" already exists on "' + (newParentNode.data ? newParentNode.data.properties.name : 'root') + '"');
            return true;
        }
        return false
    }

    checkNodeExistsInTemplate(newRow) {
        if (!this.isCheckpointGroup(newRow) && this.existsInTemplate(newRow)) {
            new Toast(this.toastController).showToast('"' + newRow.properties.name + '" already exists on the template');
            return true;
        }
        return false
    }

    existsInParent(newRow) {
        return this.rowData.find(row => this.getRowNodeWithUpdatedId(row) === this.getRowNodeWithUpdatedId(newRow));
    }
    existsInTemplate(newRow) {
        return this.rowData.find(row => row.properties.id === newRow.properties.id);
    }

    moveInArray(arr, old_index, new_index) {
        if (new_index >= arr.length) {
            var k = new_index - arr.length + 1;
            while (k--) {
                arr.push(undefined);
            }
        }
        arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    };

    moveToPath(newParentPath, node, allUpdatedNodes, oldParentData?, newParentData?) {
        var newChildPath = this.updateNodePath(node.data, newParentPath);
        if (newParentData) this.updateParents(node.data, oldParentData, newParentData);


        allUpdatedNodes.push(node.data);
        if (oldParentData) allUpdatedNodes.push(oldParentData);
        if (newParentData) allUpdatedNodes.push(newParentData);

        if (node.childrenAfterGroup) this.moveChildrenPaths(node.childrenAfterGroup, newChildPath, allUpdatedNodes);
    }

    updateNodePath(nodeData, newParentPath) {
        var oldPath = nodeData.properties.orgHierarchy;
        var fileName = oldPath[oldPath.length - 1];
        var newChildPath = newParentPath.slice();
        newChildPath.push(fileName);
        nodeData.properties.orgHierarchy = newChildPath;
        return newChildPath;
    }

    updateParents(nodeData, oldParentData, newParentData) {
        nodeData.properties.parentId = newParentData.properties.id;
        if (this.isCheckpointGroup(nodeData)) {
            if (oldParentData) oldParentData.subGroups = oldParentData.subGroups.filter(subGroup => subGroup.properties.id !== nodeData.properties.id);
            if (newParentData && newParentData.subGroups !== undefined) newParentData.subGroups.push(nodeData);
        } else {
            if (oldParentData) oldParentData.properties.checkpointList = oldParentData.properties.checkpointList.filter(checkpoint => checkpoint.properties.id !== nodeData.properties.id);
            if (newParentData && newParentData.properties.checkpointList) newParentData.properties.checkpointList.push(nodeData);
        }
    }

    moveChildrenPaths(children, newChildPath, allUpdatedNodes) {
        children.forEach((child) => {
            this.moveToPath(newChildPath, child, allUpdatedNodes);
        });
    }

    isInvalidMoveTo(selectedNode, targetNode) {
        let isInvalid = false;
        if (targetNode.parent && targetNode.parent.data) {
            isInvalid = this.isInvalidMoveTo(selectedNode, targetNode.parent);
            if (this.getRowNodeWithUpdatedId(targetNode.parent.data) === this.getRowNodeWithUpdatedId(selectedNode.data)) isInvalid = true;
        }
        if (targetNode && targetNode.data && this.getRowNodeWithUpdatedId(targetNode.data) === this.getRowNodeWithUpdatedId(selectedNode.data)) isInvalid = true;

        return isInvalid;
    }

    arePathsEqual(path1, path2) {
        let newPathLength = path1.length + 1;
        let oldPathLength = path2.length;
        if (this.isControlPressed && newPathLength === 2 && oldPathLength === 1) return true;
        if (newPathLength !== oldPathLength) return false;

        var equal = true;
        path1.forEach(function (item, index) {
            if (path2[index] !== item) {
                equal = false;
            }
        });
        return equal;
    }

    reAssignSortProperty() {
        this.gridApi.forEachNode((node) => {
            node.data.properties.sort = node.childIndex + 1
        })
    };

    addRows(data) {
        let selectedGroupNode = data.selectedGroup;
        let groupIndex = selectedGroupNode ? this.rowData.indexOf(selectedGroupNode.data) + 1 : this.rowData.length;
        data.selectedRows.forEach((selectedRow, index) => {
            let newRowData = _.cloneDeep(selectedRow.data);
            newRowData.orgHierarchy = selectedGroupNode ? _.cloneDeep(selectedGroupNode.data.properties.orgHierarchy) : [];
            newRowData.orgHierarchy.push(newRowData.name);
            newRowData = { properties: newRowData };
            newRowData.properties.parent = selectedGroupNode ? selectedGroupNode.data.properties.id : 0;
            if (this.isCheckpointGroup(newRowData)) {
                if (!this.checkNodeExistsInParent(newRowData, selectedGroupNode)) {
                    this.newGroupsCounter--;
                    newRowData.properties.checkpointGroupId = newRowData.properties.id;
                    newRowData.properties.id = this.newGroupsCounter;
                    newRowData.subGroups = [];
                    if (selectedGroupNode && selectedGroupNode.data) selectedGroupNode.data.subGroups.push(newRowData);
                    this.rowData.splice(groupIndex + index + (selectedGroupNode ? selectedGroupNode.allChildrenCount : 0), 0, newRowData)
                }
            } else {
                if (!this.checkNodeExistsInTemplate(newRowData)) {
                    newRowData.properties.templateCheckpointGroupCheckpointId = -1;
                    if (selectedGroupNode && selectedGroupNode.data) selectedGroupNode.data.properties.checkpointList.push(newRowData);
                    this.rowData.splice(groupIndex + index + (selectedGroupNode ? selectedGroupNode.allChildrenCount : 0), 0, newRowData)
                }
            }
        });
        if (selectedGroupNode) data.selectedGroup.expanded = true;
        this.updateRowData(this.rowData);
    }

    deleteRow(row) {
        if (row.properties.checkpointList && row.properties.checkpointList.length > 0) {
            row.properties.checkpointList.forEach(checkpoint => {
                this.deleteRow(checkpoint);
            });
        }
        if (row.subGroups && row.subGroups.length > 0) {
            row.subGroups.forEach(subGroup => {
                this.deleteRow(subGroup);
            });
        }
        this.deleteByUpdatedParentId(row);
        this.updateRowData(this.rowData);
    }

    deleteByUpdatedParentId(row) {
        this.rowData = this.rowData.filter(existingRow => this.getRowNodeWithUpdatedId(existingRow) !== this.getRowNodeWithUpdatedId(row));
    }

    adjustColumnsToFitWindow() {
        if (this.gridApi) this.gridApi.sizeColumnsToFit();
    }

    onFilterTextBoxChanged() {
        this.gridApi.setQuickFilter(this.quickFilterInput.nativeElement.value);
    }

    exportCsv() {
        let params = {
            fileName: 'Rows',
            sheetName: 'Rows',
            columnSeparator: ';'
        };
        this.gridApi.exportDataAsCsv(params);
    }

    print() {
        this.setPrinterFriendly();
        this.printPending = true;
        // if (this.gridApi.isAnimationFrameQueueEmpty()) {
        this.onAnimationQueueEmpty({ api: this.gridApi });
        // }
    }

    onAnimationQueueEmpty(event) {
        if (this.printPending) {
            this.printPending = false;
            this.printTable();
        }
    }

    printTable() {
        // let rest = <any>document.querySelector("ion-content :not(#grid)");
        // rest.style.display = 'none';
        print();
    }

    setPrinterFriendly() {
        let eGridDiv = <any>document.getElementById("grid");
        let preferredWidth = this.gridApi.getPreferredWidth();
        preferredWidth += 2;
        eGridDiv.style.width = preferredWidth + "px";
        eGridDiv.style.height = "";
        this.gridApi.setGridAutoHeight(true);
    }

}

html:

    <ion-content padding>
      <ion-item class="generic-tools-wrapper">
        <div>
          <input type="text" #quickFilterInput placeholder="Filter..." (input)="onFilterTextBoxChanged()" />
          <button ion-button small type="button" icon-only (click)="exportCsv()">
            <ion-icon name="list-box"></ion-icon>
          </button>
          <button ion-button small type="button" icon-only (click)="print()">
            <ion-icon name="print"></ion-icon>
          </button>
          <button ion-button small [color]="isControlPressed ? 'danger': 'primary'" type="button" icon-only (click)="isControlPressed = !isControlPressed">
            <ion-icon name="return-right"></ion-icon>
          </button>
        </div>
      </ion-item>
      <ion-label> <strong>Attention!</strong> The grouping option is <strong>{{isControlPressed ? 'deactivated':
          'activated'}}</strong>,
        elements
        dragged over a group will be put <strong>{{isControlPressed ? 'next to': 'inside'}} that group</strong>. Press
        <strong>Control</strong> to switch this option. </ion-label>
      <div style="height: 90%;box-sizing: border-box; ">
        <ag-grid-angular id="grid" style="width: 100%; height: 100%;" class="ag-theme-material" [rowData]="rowData"
          [columnDefs]="columnDefs" [gridOptions]="gridOptions" (gridReady)="onGridReady($event)" (rowDragMove)="onRowDragMove($event)"
          (rowDragEnd)="onRowDragEnd($event)" (rowDragLeave)="onRowDragLeave($event)" (viewportChanged)="adjustColumnsToFitWindow()">
        </ag-grid-angular>
      </div>
    </ion-content>

0 个答案:

没有答案