0

I'm creating horizontal tree from nested array. After tree is created, the links from one node to another are regularly being updated, but the removal functionality is not working.

import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';


@Component({
  selector: 'app-rtm-graph',
  templateUrl: './rtm-graph.component.html',
  styleUrls: ['./rtm-graph.component.css']
})
export class RtmGraphComponent implements OnInit {

  private rtmData: {id:number, name: string, children: any[] } =
    { id: 0, name: 'RTM', children: [
      { id: 0, name: 'RTM', children: [
        { id: 0, name: 'RTM', children: []},
        { id: 0, name: 'RTM', children: []},
        { id: 0, name: 'RTM', children: []},
      ]},
      { id: 0, name: 'RTM', children: []},
    ] };

  private svg: any;
  private margin = 50;
  private width = 750 - (this.margin * 2);
  private height = 750 - (this.margin * 2);
  private root: any;
  private treemap = d3.tree().size([this.height, this.width])
  private duration = 1124;
  private i = 0;

  constructor(public _rs:RootService) { }


  ngOnInit(): void {
    this.createSvg();
    this.createTree()
  }
  private callSvg() {
    return d3.select("figure#rtm-graph");
  }
  private createTree(): void {
    this.root = d3.hierarchy(this.rtmData, (d) => {
      return d.children;
    })

    this.root.x0 = this.height / 2;
    this.root.y0 = 0;
    this.update(this.root);

  }
  private createSvg(): void {
    this.svg = this.callSvg()
      .append("svg")
      .attr("width", this.width + (this.margin * 2))
      .attr("height", this.height + (this.margin * 2))
      .append("g")
      .attr("transform", `translate(${this.margin},${this.margin})`);
  }

  private update(source): void {
    let treedata = this.treemap(this.root);
 
    let nodes = treedata.descendants();

    nodes.forEach(d => {
      d.y = d.depth * this.width / 5;
    });
    let node = this.svg.selectAll("g.node").data(nodes, (d) => d.id || (d.id = ++this.i));

    // links

    let links = treedata.descendants().slice(1);
    let link = this.svg.selectAll('path.link').data(links, (d) => {
      return d.id;
    })
    let linkEnter = link
      .enter()
      .insert('path', 'g')
      .attr('class', 'links')
      .attr('d', (d) => {
        let o = { x: source.x0, y: source.y0 + 40 }
        return this.diagonal(o, o)
      })
    let linkUpdate = linkEnter.merge(link);
    linkUpdate
      .transition()
      .duration(this.duration)
      .attr("d", (d) => {
        return this.diagonal(d, d.parent);
      });


    link
      .exit()
      .transition()
      .attr('d', (d: any) => {
        console.log("Inside link exit")
        let o = { x: source.x0, y: source.y0 }
        return this.diagonal(o, o);
      })
      .remove();

    let nodeEnter = node
      .enter()
      .append("g")
      .attr("class", "node")
      .attr("transform", d => {
        return `translate(${source.y0 + 20},${source.x0})`
      })
    .on("click", this.clicked.bind(this))



    nodeEnter.append('circle')
      .attr('class', 'node')
      .attr('r', 0)
      .style('fill', d => {
        return d._children ? "red" : "white";
      })


    let nodeUpdate = nodeEnter.merge(node);

    nodeUpdate.transition()
      .duration(this.duration)
      .attr("transform", d => `translate(${d.y + 20},${d.x})`)
      .attr("opacity", 1)

    nodeUpdate.select("circle.node")
      .attr('r', 10)
      .style("fill", d => d._children ? "red" : "black")
      .attr("cursor", "pointer");

    nodeUpdate.append('rect')
      .attr('x', 0)
      .attr('y', -20)
      .attr('rx', 5)
      .attr('ry', 5)
      .attr('width', 80)
      .attr('height', 40)
      .attr('fill', 'grey')
      .exit();

    nodeUpdate.append('text')
      .attr('x', 0)
      .attr('y', 0)
      .attr('dx', 10)
      .text(d => {
        console.log(d.data.name)
        return d.data.name;
      });


    let nodeExit = node.exit()
      .transition()
      .duration(this.duration)
      .attr("transform", function () { return `translate(${source.y + 20},${source.x})` })
      .attr("opacity", 0.5)
      .remove();

  
    // collapsing of the nodes
    nodes.forEach(d => {
      d.x0 = d.x;
      d.y0 = d.y;
    })
  }

  diagonal(s, d) {
    let path = `M ${s.y} ${s.x}
          C ${(s.y + d.y) / 2} ${s.x}
            ${(s.y + d.y) / 2} ${d.x}
            ${d.y} ${d.x}`;
    return path;
  }

  clicked(event, d) {
    console.log('ddddd:::',d);
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    this.update(d);
  }
}

If I add console.log("Inside link exit") to the code, then no log is written to the console. But if I run same code in vanilla JavaScript, then I get the expected output from this console.log.

For the first time, the graph is generated correctly. enter image description here

but after collapsing nodes, the output is as follows: enter image description here

How to remove this residual link?

Hölderlin
  • 424
  • 1
  • 3
  • 16

0 Answers0