简体   繁体   English

D3可视化-更新ngOnChanges(Angular / TypeScript)

[英]D3 Visualization - Updating in ngOnChanges (Angular / TypeScript)

I'm using this d3 bl.ocks example in my Angular project , though I'm trying to update the data onChanges instead of inside the buildViz function. 我正在Angular项目中使用此d3 bl.ocks示例 ,尽管我试图更新数据onChanges而不是内部buildViz函数。 I've mocked this in the example below by setting an interval in ngOnInit. 在下面的示例中,我通过在ngOnInit中设置一个间隔来对此进行嘲笑。

I'm getting an error that it can't find the node data ( ERROR TypeError: Cannot read property 'data' of undefined ). 我收到一条错误消息,它找不到节点数据( ERROR TypeError: Cannot read property 'data' of undefined )。 I'm guessing this is because the function is being called outside of buildViz function itself, though passing in the nodes and links did not seem to resolve the issue. 我猜这是因为该函数在buildViz函数本身之外被调用,尽管传入节点和链接似乎无法解决问题。 Any thoughts? 有什么想法吗?

Error is coming from this line in my restart function: 我的重启功能中的这一行出现错误:

node = node.data(_this.nodes, function (d) { return d.id; });

Working Code: StackBlitz 工作代码: StackBlitz

My code: 我的代码:

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

@Component({
  selector: 'd3-viz',
  templateUrl: './d3-viz.html'
})
export class D3Viz {

  private host;

  width: number = 750;
  height: number = 500;

  a = { id: "a" };
    b = { id: "b" };
    c = { id: "c" };

  links = [
    { source: this.a, target: this.b },
    { source: this.b, target: this.c },
    { source: this.c, target: this.a }
  ];

  nodes = [this.a, this.b, this.c];

  constructor(private element: ElementRef) {
    this.host = d3.select(this.element.nativeElement);
  }

  ngOnInit() {
    this.buildViz();

      d3.interval(() => {
        this.nodes.pop(); // Remove c.
        this.links.pop(); // Remove c-a.
        this.links.pop(); // Remove b-c.
        this.buildViz('update');
      }, 2000, d3.now());

      d3.interval(() => {
        this.nodes.push(this.c);
        this.links.push({ source: this.b, target: this.c });
        this.links.push({ source: this.c, target: this.a });
        this.buildViz('update');
      }, 2000, d3.now() + 1000);

  }

    buildViz(update?) {

      let svg = this.host.append('svg')
        .attr('width', this.width)
        .attr('height', this.height);
      let color = d3.scaleOrdinal(d3.schemeCategory10);


      if(!update){

      var simulation = d3.forceSimulation<any>(this.nodes)
        .force("charge", d3.forceManyBody().strength(-1000))
        .force("link", d3.forceLink(this.links).distance(200))
        .force("x", d3.forceX())
        .force("y", d3.forceY())
        .alphaTarget(1)
        .on("tick", ticked);

      var g = svg.append("g").attr("transform", "translate(" + this.width / 2 + "," + this.height / 2 + ")"),
        link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
        node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");

    }



      var restart = () => {

        // Apply the general update pattern to the nodes.
        node = node.data(this.nodes, function (d: any) { return d.id; });
        node.exit().remove();
        node = node.enter().append("circle").attr("fill", function (d: any) { return color(d.id); }).attr("r", 8).merge(node);

        // Apply the general update pattern to the links.
        link = link.data(this.links, function (d) { return d.source.id + "-" + d.target.id; });
        link.exit().remove();
        link = link.enter().append("line").merge(link);

        // Update and restart the simulation.
        simulation.nodes(this.nodes);
        simulation.force<any>("link").links(this.links);
        simulation.alpha(1).restart();
      }
      restart();


      function ticked() {
        node.attr("cx", function (d: any) { return d.x; })
          .attr("cy", function (d: any) { return d.y; })

        link.attr("x1", function (d: any) { return d.source.x; })
          .attr("y1", function (d: any) { return d.source.y; })
          .attr("x2", function (d: any) { return d.target.x; })
          .attr("y2", function (d: any) { return d.target.y; });
      }
    }

}

ngOnChanges is a lifecycle hook that is triggered when an Input changes (input as an Angular input, with an @Input decorator). ngOnChanges是生命周期挂钩, 当输入发生更改 (输入为带有@Input装饰器的Angular输入) 触发。

I don't really know your goal there, so I can't give you a solution, but at least I can tell you why it is not working. 我真的不知道您在那儿的目标,因此我无法为您提供解决方案,但至少我可以告诉您为什么它不起作用。

As for your error, you write this : 至于您的错误,您可以这样写:

  d3.interval(function () {
    this.nodes.pop(); // Remove c.
    this.links.pop(); // Remove c-a.
    this.links.pop(); // Remove b-c.
    this.buildViz(this.nodes, this.links, 'update');
  }, 2000, d3.now());

When you use the this keyword in a function, it references the function, not your component. 在函数中使用this关键字时,它引用函数,而不是组件。 To correct that, use this instead : 要更正此问题,请改用此命令:

  d3.interval(() => {
    this.nodes.pop(); // Remove c.
    this.links.pop(); // Remove c-a.
    this.links.pop(); // Remove b-c.
    this.buildViz(this.nodes, this.links, 'update');
  }, 2000, d3.now());

this is the fat arrow notation, you should check out Google for more information about that. 这是粗箭头符号,您应该查看Google以获得更多信息。

And for other functions, such as function restart(nodes, links) , you should write it restart = (nodes, links) => {...} if you want to keep the context of this bound to your class. 对于其他函数,例如function restart(nodes, links) ,如果要将其上下文绑定到您的类,则应将其写为restart = (nodes, links) => {...}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM