将D3 JS树示例转换为Angular 4-节点和链接

时间:2019-05-09 15:25:53

标签: javascript angular typescript d3.js

所以我讨厌我问这个问题,但是我已经到达了“不能见树木换树木”的阶段,这是最后的手段。 我试图举这个例子:http://plnkr.co/edit/P2Jcqh12cxVyToLdXun6?p=preview并放入Angular应用程序。一开始就有一些小问题。我的JS和Angular知识不是世界上最好的,并且该示例是在D3的早期版本中编写的,但对功能的一些主要部分进行了更改,即对角线投影以及查找节点和链接。 初始节点和按钮正确显示,但是我不得不将rightCliCk函数更改为:

onRightClick = (d) => {
    event.stopPropagation();
    var childName = "temp";

    if(typeof d.children === 'undefined'){
      var newChild = [{
        "name": childName,
        "parent": "Son of A",
      }];

    var newnode = d3.hierarchy(newChild);
    d.children = newnode;

    d.children = [];
    d.children.push(newnode);

    //console.log(d.children);
    this.update(d);

    } else {
      var newChild = [{
        "name": childName,
        "parent": "Son of A",
      }];
      console.log(d.children);
      d.children.push(newChild);
      console.log(d.children);
      this.update(d);
    };
  };

这是基于对该主题进行的一些研究。但是,当我返回链接的更新功能时,出现错误“无法读取未定义的属性'id'”。

//Update the links
    var link = this.svg.selectAll('path.link')
      .data(links, (d) => { return d.target.id; })

我认为香港专业教育学院在这里错过了一些东西,或者我没有得到D3过去用来对抗现在工作方式的方式。

完整的TS文件:

import { Component, OnInit, HostListener } from '@angular/core';
import * as d3 from 'd3';
import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {
  @HostListener('window:resize', ['$event'])
  onResize(event?){
    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;
  }

  title = 'angular-d3-tree-example';
  margin: any;
  width: number;
  height: number;
  scale: number;
  svg: any;
  duration: number;
  root: any;
  tree: any;
  rightTree: any;
  leftTree: any;
  treeData: any;
  nodes: any;
  links: any;
  screenHeight: number;
  screenWidth: number;
  oldrx;
  oldry;

  constructor(private appservice: AppService) { 
    this.onResize();
  }

  ngOnInit(): void {
    //this.treeData = this.appservice.fetchData();

    this.treeData = [{
      "name": "Device",
      "parent": "null"
    }];
    this.setData();
  }
  setData(){
    //Set the dimensions and margins of the diagram
    this.margin = { top: 20, right: 120, bottom: 20, left: 120 };
    this.width = this.screenWidth - this.margin.left - this.margin.right;
    this.height = this.screenHeight - this.margin.top - this.margin.bottom - 20;
    this.scale = 1;
    this.duration = 750;

    // declares a tree layout and assigns the size
    this.tree = d3.cluster().size([this.height, this.width]);

    //append the svg object to the body of the page
    //appends a 'group' element to 'svg'
    //moves the 'group' element to the top left margin
    this.svg = d3.select('body')
      .append('svg')
        .attr('width', this.width + this.margin.right + this.margin.left)
        .attr('height', this.height + this.margin.top + this.margin.bottom)
      //.append("g")
        //.attr('transform', 'translate(' + this.width/2 + this.height / 2  + ')');

    this.makeRightTree();
  }

  makeRightTree = () => {
    this.rightTree = d3.cluster().size([this.height, this.width]);

    this.svg = d3.select('svg')
      .append('g').attr('transform', 'translate(600,0)');

    this.root = d3.hierarchy(this.treeData, (d) => { return d.children;});
    this.oldrx = this.root.x0 = this.height/2;
    this.oldry = this.root.y0 = 0;

    this.update(this.root);
  }

  update = (source) => {
    var i = 0;

    //Assigns the x and y position for the nodes
    this.treeData = this.tree(this.root);

    //Compute the new tree layout
    var nodes = this.treeData.descendants();
    var links = this.treeData.descendants().slice(1);

    //Normalise for fixed-depth
    nodes.forEach((d) => { d.y = d.depth * 180; });

    //Update Nodes
    var node = this.svg.selectAll('g.node')
      .data(nodes, (d) => { return d.id || (d.id = ++i); });

    //Enter any new nodes at the parents previous position
    var nodeEnter = node.enter().append('g')
      .attr('class', (d) => {
        if(d.parent == "null"){
          return "node rightparent";
        } else {
          return "node rightchild";
        }
      })
      .attr('transform', (d) => {
        return 'translate(' + source.y0 + ',' + source.x0 + ")";
      })
      //.on('click', this.click);

    //Node Shapes
    nodeEnter.append('rect')
      .attr('x', '-10')
      .attr('y', '-15')
      .attr('height', 30)
      .attr('width', 100)
      .attr('rx', 15)
      .attr('ry', 15)
      .style('fill', '#f1f1f1');

    //Node Text
    nodeEnter.append('text')
      .attr('x', (d) => { return d.children || d._children ? -13 : 13; })
      .attr('dy', '.35em')
      .attr('text-anchor', (d) => { return d.children || d._children ? 'end' : 'start'; })
      .text((d) => { return d.name; })
      .style('fill-opacity', 1e-6);

    //Buttons - circle then the plus +
    var addRightChild = nodeEnter.append('g');
    addRightChild.append('rect')
      .attr('x', '90')
      .attr('y', '-10')
      .attr('height', 20)
      .attr('width', 20)
      .attr('rx', 10)
      .attr('ry', 10)
      .style('stroke', '#444')
      .style('stroke-width', '2')
      .style('fill', '#ccc');

    addRightChild.append('line')
      .attr('x1', 95)
      .attr('y1', 1)
      .attr('x2', 105)
      .attr('y2', 1)
      .attr('stroke', '#444')
      .style('stroke-width', '2');

    addRightChild.append('line')
      .attr('x1', 100)
      .attr('y1', -4)
      .attr('x2', 100)
      .attr('y2', 6)
      .attr('stroke', '#444')
      .style('stroke-width', '2');

    //Button click
    addRightChild.on('click', this.onRightClick);

    //Transition nodes to their new position
    var nodeUpdate = node.transition()
      .duration(this.duration)
      .attr('transform', (d) => {
        if(d.parent == "null") {
          d.y = this.oldry;
          d.x = this.oldrx;
        }
      });

    nodeUpdate.select('circle')
      .attr('r', 10)
      .style('fill', (d) => { return d._children ? 'lightsteelblue' : '#fff'; });

    nodeUpdate.select('text')
      .style('fill-opacity', 1);

    //Transition exiting nodes to the parents new position
    var nodeExit = node.exit().transition()
      .duration(this.duration)
      .attr('transform', (d) => { return 'translate(' + source.y + ',' + source.x + ')'; })
      .remove();

    nodeExit.select('circle')
      .attr('r', 1e-6);

    nodeExit.select('text')
      .style('fill-opacity', 1e-6);

    //Update the links
    var link = this.svg.selectAll('path.link')
      .data(links, (d) => { return d.target.id; })

    //Enter any new links at the parents previous position
    link.enter().insert('path', 'g')
      .attr('class', 'link')
      .style('stroke', 'black')
      .attr('d', (d) => {
        var o = { x: source.x0, y: source.y0 };
        return this.diagonal(o,o);
      })
      .on('click', this.removeLink);

    //Transition links to their new position
    link.transition()
      .duration(this.duration)
      .attr('d', this.diagonal);

    //transition exiting nodes to parents new position
    link.exit().transition()
      .duration(this.duration)
      .attr('d', (d) => {
        var o = { x: source.x, y: source.y };
        return this.diagonal(o,o);
      })
      .remove();

    //Stash the old positions for transition
    nodes.forEach((d) => {
      d.x0 = d.x;
      d.y0 = d.y;
    });


  };

  onRightClick = (d) => {
    event.stopPropagation();
    var childName = "temp";

    if(typeof d.children === 'undefined'){
      var newChild = [{
        "name": childName,
        "parent": "Son of A",
      }];

    var newnode = d3.hierarchy(newChild);
    d.children = newnode;

    d.children = [];
    d.children.push(newnode);

    //console.log(d.children);
    this.update(d);

    } else {
      var newChild = [{
        "name": childName,
        "parent": "Son of A",
      }];
      console.log(d.children);
      d.children.push(newChild);
      console.log(d.children);
      this.update(d);
    };
  };

  removeLink = (d) => {
    //This is the links target node which you want to remove
    var target = d.target;
    //make new set of children
    var children = [];
    //iterate through the children
    target.parent.children.forEach((child) => {
      if(child.id != target.id){
        //add to the child list if target id is not same
        //so that the node target is removed
        children.push(child);
      }
    });
    //set the target parent with new set of children sans the one which is removed
    target.parent.children = children;
    //redraw the parent since one of its children is removed
    this.update(d.target.parent);
  }

  //Creates a curved (diagonal) path from parent to the child nodes
  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;
  }
}

0 个答案:

没有答案