繁体   English   中英

在 Angular 6 中使用 D3.js + 单击事件

[英]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.

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