-1

Im trying to build a table with nested tree folder inside.

When trying to add nested data into the datasource data the structure will not updated and will not toggle anymore.

Code below:

https://stackblitz.com/edit/angular-table-tree-example-k2zqmt?file=app%2Ftable-basic-example.ts&file=app%2Ftable-basic-example.html,app%2Ftable-basic-example.ts

Environment

Angular:
Material Table
Material tree system

1 Answers1

0

These are the things that are happening when logNode method is called

  • The item is getting added but the treeControl.toggle method does not work anymore.
  • When you are assigning a new dataset to the dataSource all the nodes get reset and the tree closes, so this.treeControl.toggle is trying to toggle a node that does not exist.
  • You need to find the node to be toggled from the list you get from treeControl.dataNodes

I would suggest having the toggle code in a separate method and adding a node code in a separate method, and a separate button to add the node.

The below code should work for your scenario, also remove this line from your HTML, (click)="treeControl.toggle(data)"


interface ExampleFlatNode {
  expandable: boolean;
  RoleName: string;
  Access: boolean;
  level: number;
  CatId: number;
}

private transformer = (node: FoodNode, level: number) => {
  return {
    expandable:
      !!node.CategoryPermissions && node.CategoryPermissions.length > 0,
    RoleName: node.RoleName,
    Access: node.Access,
    level: level,
    CatId: node.CatId,
  };
};

tempNodes = []

constructor() {
  this.dataSource.data = TREE_DATA;
}

logNode(clickedNode) {

    this.tempNodes = [];

    this.treeControl.dataNodes.forEach((node) =>
      this.tempNodes.push({
        ...node,
        expanded: this.treeControl.isExpanded(node),
      })
    );

  if (!this.treeControl.isExpanded(clickedNode)) {
    const temp = {
      Access: true,
      RoleName: 'test 1 2',
      CatId: 113,
    };

    const clickedNodeIdx = this.treeControl.dataNodes.findIndex(
      (node: any) =>
        node.CatId === clickedNode.CatId &&
        node.RoleName === clickedNode.RoleName &&
        node.level === clickedNode.level
    );

    const childIdx = 1;

    let child;

    if (clickedNode.level === 0) {
      child =
        this.dataSource.data[clickedNodeIdx].CategoryPermissions[childIdx];
    } else {
      this.dataSource.data.forEach(
        (item) => (child = this.findDataSource(item, clickedNode))
      );
    }

    child.CategoryPermissions.push(temp);

    this.dataSource.data = this.dataSource.data;

    const addedNode = this.treeControl.dataNodes.find(
      (node: any) =>
        node.CatId === temp.CatId && node.RoleName === temp.RoleName
    );

    this.expandParent(addedNode);
    this.setPreviousState();

  } else {
    this.treeControl.collapse(clickedNode);
  }
}

findDataSource(item, node) {
  if (item.RoleName === node.RoleName) {
    return item;
  } else if (item.CategoryPermissions) {
    let matchedItem;

    item.CategoryPermissions.forEach((e) => {

      const temp = this.findDataSource(e, node);
      if (temp) {
        matchedItem = temp;
      }
    });

    return matchedItem;
  }
}

setPreviousState() {
  for (let i = 0, j = 0; i < this.treeControl.dataNodes.length; i++) {
    if (
      this.tempNodes[j] &&
      this.treeControl.dataNodes[i].RoleName === this.tempNodes[j].RoleName &&
      this.treeControl.dataNodes[i].CatId === this.tempNodes[j].CatId &&
      this.treeControl.dataNodes[i].level === this.tempNodes[j].level
    ) {
      if (this.tempNodes[j].expanded) {
        this.treeControl.expand(this.treeControl.dataNodes[i]);
      }
      j++;
    }
  }
}

expandParent(node: ExampleFlatNode) {
  const { treeControl } = this;
  const currentLevel = treeControl.getLevel(node);

  const index = treeControl.dataNodes.indexOf(node) - 1;

  for (let i = index; i >= 0; i--) {
    const currentNode = treeControl.dataNodes[i];

    if (currentLevel === 0) {
      this.treeControl.expand(currentNode);
      return null;
    }

    if (treeControl.getLevel(currentNode) < currentLevel) {
      this.treeControl.expand(currentNode);
      this.expandParent(currentNode);
      break;
    }
  }
}
Mr. Stash
  • 2,940
  • 3
  • 10
  • 24
  • The requirement is that after clicking the arrow should call an API and fill the data for that row. –  Oct 23 '22 at 04:30
  • How will the tree close? or will the item be added only on expansion, and will the items get added to the clicked node or the child node as in the example? – Mr. Stash Oct 23 '22 at 06:43
  • Yes the item will added to child node only. Im trying to create a fake icon arrow based on some boolean condition. After item clicked will call the API and will push the data as have mentioned up at addNode() . I tried your method but yet the parent gets closed. –  Oct 23 '22 at 10:45
  • 1
    I have updated the code, it is linear but should work for adding items when clicked on the top level. – Mr. Stash Oct 23 '22 at 16:04
  • Thank you. Is working. Will update on code for nodes as well –  Oct 23 '22 at 16:37
  • Just adopted the code to add elements on sub level node but yet there is the issue that after adding the top level node will be collapsed. That happening due to this.dataSource.data = this.dataSource.data –  Oct 23 '22 at 17:47
  • Re: Adding this.treeControl.toggle(this.treeControl.dataNodes[0]); Will affect the nodes of that level to be collapsed –  Oct 23 '22 at 18:04
  • if you want to add the child you need to expand the parent items, I have updated the answer, and since the 2nd level in your example does not have a child I have added the new nodes directly to the next lvl, please adjust that and the level based logic to get the child – Mr. Stash Oct 23 '22 at 19:33
  • Just updated the code. Solution working but yet nodes dont save the state of being expanded. When adding to some subnode the others will be closed. –  Oct 24 '22 at 17:30
  • 1
    I added a method to set the previous state – Mr. Stash Oct 25 '22 at 10:03