【问题标题】:D3 Angular TreeD3 角树
【发布时间】:2019-07-24 18:32:25
【问题描述】:

我有一个正在工作的 D3 树,我正在尝试将其转换为 Angular。这是一个 v3 树。

组件代码为:

import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation } from '@angular/core';
declare var d3: any;

@Component({
  selector: 'app-tree-view',
  template: '<div #chart id="chart"></div>',
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['./tree-view.component.scss']
})
export class TreeViewComponent implements AfterViewInit, OnInit {

  ngOnInit(): void {
    this.createChart();
  }

  constructor(){

  }

  ngAfterViewInit(): void {
    this.root.update = this.update;
    this.update(this.root);
  }

private treeData: any[] = [{...data here}],

tree: any;
i = 0;
duration = 750;
root:any = {};
svg:any = {};
diagonal: any;

@ViewChild('chart')
private chartContainer: ElementRef;


private createChart(): void {
  var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 1560 - margin.right - margin.left,
    height = 1000 - margin.top - margin.bottom;

 this.tree = d3.layout.tree()
    .size([height, width]);

  const element = this.chartContainer.nativeElement;

 this.diagonal = d3.svg.diagonal().projection((d) => { return [d.y, d.x]; });

 this.svg = d3.select(element).append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

this.root = this.treeData[0];
this.root.x0 = height / 2;
this.root.y0 = 0;

this.hideChildren(this.root);

console.log(this.root);

d3.select(self.frameElement).style("height", "1000px");
}

 private hideChildren(node) {
    if(node.children) {
        node._children = node.children;
        node.children = null;
        node._children.forEach((a) =>  { this.hideChildren});
    }
}

 private update(source) {

  // Compute the new tree layout.
  var nodes = this.tree.nodes(this.root).reverse(),
      links = this.tree.links(nodes);

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

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

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

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", (d) => { return d._children ? "lightsteelblue" : "#fff"; });

  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);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
      .duration(this.duration)
      .attr("transform", (d) => { return "translate(" + d.y + "," + d.x + ")"; });

  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 parent's 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 parent's previous position.
  link.enter().insert("path", "g")
      .attr("class", "link")
      .attr("d", (d) => {
        var o = {x: source.x0, y: source.y0};
        return this.diagonal({source: o, target: o});
      });

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

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

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

  console.log('end creating chart');
}

// Toggle children on click.
click(d, item) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }

  this.update(d);
}


}

脚本使用

编译成angular.json文件

"scripts": ["src/scripts/d3.js"]

但是,当我展开一个节点时,我得到一个错误

5core.js:14597 错误类型错误:this.update 不是函数 在 SVGGElement.push../src/app/tree-view/tree-view.component.ts.TreeViewComponent.click (树视图.component.ts:69898) 在 SVGGElement.__onclick (d3.js:1) 在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423) 在 Object.onInvokeTask (core.js:16147) 在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422) 在 Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195) 在 ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [作为调用] (zone.js:498) 在调用任务(zone.js:1744) 在 SVGGElement.globalZoneAwareCallback (zone.js:1770)

显然这是范围界定问题 - this,但我不知道如何解决这个问题。 this 指的是节点,而不是类。

任何帮助将不胜感激

干杯 KH

【问题讨论】:

    标签: angular d3.js


    【解决方案1】:

    我使用内联函数而不是成员解决了这个问题

       ngOnInit(): void {
            console.log('on init');
            const createChart = () => {
                var margin = { top: 20, right: 120, bottom: 20, left: 120 },
                    width = 1560 - margin.right - margin.left,
                    height = 1000 - margin.top - margin.bottom;
    
                this.tree = d3.layout.tree()
                    .size([height, width]);
    
                const element = this.chartContainer.nativeElement;
    
                this.diagonal = d3.svg.diagonal().projection((d) => { return [d.y, d.x]; });
    
                this.svg = d3.select(element).append("svg")
                    .attr("width", width + margin.right + margin.left)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
                this.root = this.treeData[0];
                this.root.x0 = height / 2;
                this.root.y0 = 0;
    
                hideChildren(this.root);
    
                d3.select(self.frameElement).style("height", "1000px");
            };
    
    
            const update = (source) => {
    
                // Compute the new tree layout.
                var nodes = this.tree.nodes(this.root).reverse(),
                    links = this.tree.links(nodes);
    
                // Normalize for fixed-depth.
                nodes.forEach((d) => { d.y = d.depth * 180; });
    
                // Update the nodes…
                var node = this.svg.selectAll("g.node")
                    .data(nodes, (d) => { return d.id || (d.id = ++this.i); });
    
                // Enter any new nodes at the parent's previous position.
                var nodeEnter = node.enter().append("g")
                    .attr("class", "node")
                    .attr("transform", (d) => { return "translate(" + source.y0 + "," + source.x0 + ")"; })
                    .on("click", click);
    
                nodeEnter.append("circle")
                    .attr("r", 1e-6)
                    .style("fill", (d) => { return d._children ? "lightsteelblue" : "#fff"; });
    
                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);
    
                // Transition nodes to their new position.
                var nodeUpdate = node.transition()
                    .duration(this.duration)
                    .attr("transform", (d) => { return "translate(" + d.y + "," + d.x + ")"; });
    
                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 parent's 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 parent's previous position.
                link.enter().insert("path", "g")
                    .attr("class", "link")
                    .attr("d", (d) => {
                        var o = { x: source.x0, y: source.y0 };
                        return this.diagonal({ source: o, target: o });
                    });
    
                // Transition links to their new position.
                link.transition()
                    .duration(this.duration)
                    .attr("d", this.diagonal);
    
                // Transition exiting nodes to the parent's new position.
                link.exit().transition()
                    .duration(this.duration)
                    .attr("d", (d) => {
                        var o = { x: source.x, y: source.y };
                        return this.diagonal({ source: o, target: o });
                    })
                    .remove();
    
                // Stash the old positions for transition.
                nodes.forEach((d) => {
                    d.x0 = d.x;
                    d.y0 = d.y;
                });
    
                console.log('end creating chart');
            };
    
    
            const hideChildren = (node) => {
                if (node.children) {
                    node._children = node.children;
                    node.children = null;
                    node._children.forEach((a) => { hideChildren(a); });
                }
            };
    
            const click = (d) => {
                if (d.children) {
                    d._children = d.children;
                    d.children = null;
                } else {
                    d.children = d._children;
                    d._children = null;
                }
    
                update(d);
            };
    
            createChart();
            this.root.update = update;
            update(this.root);
        }
    

    【讨论】:

    • 您能否提供 Stackblitz 中的示例。我正在尝试在 Angular 9 上使用 D3 实现树可视化。我对如何实现它感到困惑。 stackblitz.com/edit/angular-ivy-fvqfze,我创建了 Stackblitz 示例,但它不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多