[英]Using D3.js + click events with Angular 6
我正在尝试在 Angular 中使用 d3.js v4 创建树/层次结构数据结构可视化。 我基本上只是将这个实现https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd复制到一个组件中,并且遇到了“点击展开/收缩”功能的问题。 可视化在刷新时按预期呈现,但是当我单击节点时,出现此错误:
ERROR TypeError: this.update is not a function
Stack trace:
./src/app/components/data-displayer.component.ts/DataDisplayerComponent.prototype.click@http://localhost:4200/main.js:304:9
contextListener/<@http://localhost:4200/vendor.js:69524:7
./node_modules/zone.js/dist/zone.js/</ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.js:2743:17
onInvokeTask@http://localhost:4200/vendor.js:33761:24
./node_modules/zone.js/dist/zone.js/</ZoneDelegate.prototype.invokeTask@http://localhost:4200/polyfills.js:2742:17
./node_modules/zone.js/dist/zone.js/</Zone.prototype.runTask@http://localhost:4200/polyfills.js:2510:28
./node_modules/zone.js/dist/zone.js/</ZoneTask.invokeTask@http://localhost:4200/polyfills.js:2818:24
invokeTask@http://localhost:4200/polyfills.js:3862:9
globalZoneAwareCallback@http://localhost:4200/polyfills.js:3888:17
core.js:1598
似乎当 onclick 事件被触发时,子程序 (this.click) 再也看不到类的其余部分(我尝试记录类字段,这些字段都被记录为未定义)。 它仍然传递了正确的数据,只是无法调用this.update
方法。 我正在使用angular: 6.0.1, Node: 8.9.1, OS: win32 x64
import { Component, Input, OnInit, ViewEncapsulation } from "@angular/core";
import * as d3 from "d3";
import { HierarchyPointNode } from "d3";
export const margin = { top: 20, right: 120, bottom: 20, left: 120 };
export const width = 960 - margin.right - margin.left;
export const height = 800 - margin.top - margin.bottom;
@Component({
selector: "data-displayer",
template: "<svg></svg>",
styleUrls: ["data-displayer.component.css"],
providers: [],
encapsulation: ViewEncapsulation.None,
})
export class DataDisplayerComponent implements OnInit {
private svg;
private treeLayout;
private root;
ngOnInit() {
d3.json("../../assets/flare.json").then(data => {
this.root = d3.hierarchy(data, (d) => d.children);
this.root.x0 = height / 2;
this.root.y0 = 0;
let collapse = function (d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
this.root.children.forEach(collapse);
this.update(this.root);
});
this.svg = d3.select("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.treeLayout = d3.tree().size([height, width]);
}
update(source) {
let i = 0;
let duration = 750;
let treeData = this.treeLayout(this.root);
let nodes = treeData.descendants();
let links = treeData.descendants().slice(1);
nodes.forEach(d => d.y = d.depth * 180);
let node = this.svg.selectAll("g.node")
.data(nodes, d => d.id || (d.id = ++i) );
let nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", d => "translate(" + source.y0 + "," + source.x0 + ")")
.on("click", this.click);
nodeEnter.append("circle")
.attr("class", "node")
.attr("r", 1e-6)
.style("fill", d => d._children ? "lightsteelblue" : "#fff");
let nodeUpdate = nodeEnter.merge(node);
nodeUpdate.transition()
.duration(duration)
.attr("transform", d => "translate(" + d.y + "," + d.x + ")");
nodeUpdate.select("circle.node")
.attr("r", 10)
.style("fill", d => d._children ? "lightsteelblue" : "#fff")
.attr("cursor", "pointer");
let nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", d => "translate(" + source.y + "," + source.x + ")")
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
let link = this.svg.selectAll("path.link")
.data(links, d => d.id);
let linkEnter = link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", d => {
let o = { x: source.x0, y: source.y0 };
return this.diagonal(o, o);
});
let linkUpdate = linkEnter.merge(link);
linkUpdate.transition()
.duration(duration)
.attr("d", d => {
return this.diagonal(d, d.parent)
});
let linkExit = link.exit().transition()
.duration(duration)
.attr("d", d => {
let o = { x: source.x, y: source.y };
return this.diagonal(o, o);
})
.remove();
nodes.forEach(d => {
d.x0 = d.x;
d.y0 = d.y;
});
}
click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
this.update(d);
}
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;
}
您需要绑定this
来的onclick方法:
.on("click", this.click.bind(this));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.