4

I am trying to convert D3JS Tree Animation into AngularJS2/Typescript. I am not an expert of typescript.

Creating the basic tree animation is done and working. Now HTML contents needs to be injected in Tree Nodes.Its Something like form Wizard and as and when "Finish" and "Close" button hit, respectively the active node should be collapsed and the next node of the tree should be enabled.

Working plunk can be found here : https://plnkr.co/edit/cxuzCr?p=preview

      export class AppComponent {
    name = 'D3!';
    public margin = {
        top: 20,
        right: 120,
        bottom: 20,
        left: 120
    };

    width = 1400 - this.margin.right - this.margin.left;
    height = 800 - this.margin.top - this.margin.bottom;

    public inst: number = 1;
    public duration: number = 750;
    public rectW: number = 60;
    public rectH: number = 30;
    public zm: any;
    public collapse: Function;
    public d: any;
    public error: any;
    public view: any;
    public parent: any;
    public visitFn: any;
    public childrenFn: any;
    public links: any;
    public tree: any;
    public maxLabelLength: any;
    public svg: any;
    public drag: any;
    public dragmove: any;

    // how much horz space to give a flaring of branches (menus, day/night behaviour)
    public BRANCH_SPACE = 80;


    constructor(private _nodeDataService: NodeDataService) {

    }

    ngOnInit() {
        this.tree = d3.layout.tree().size([this.height, this.width]);
        this.drag = d3.behavior.drag()
            .on("drag", this.dragmove);

        this.dragmove = (d) => {
            var x = d3.event.x;
            var y = d3.event.y;
            d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
        };

        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")
            .call(this.drag)
            .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");


        this.getNodes();

        //this.zm.translate([350, 20]);
    }


    diagonal = d3.svg.diagonal().projection((d: any) => {
        // return [d.x + this.rectW / 2, d.y + this.rectH / 2];
        return [d.y, d.x];
    });



    // A recursive helper function for performing some setup by walking through all nodes
    public visit = (parent: any, visitFn: any, childrenFn: any): void => {
        if (typeof childrenFn !== 'function') {
            //default childrenFn =
            childrenFn = function (node) {
                return node.children || null;
            };
        }
        if (!parent) {
            return;
        }
        visitFn(parent);
        var children = childrenFn(parent);
        if (children) {
            for (var i = 0, count = children.length; i < count; i++) {
                this.visit(children[i], visitFn, childrenFn);
            }
        }
    }
    getNodes() {
        this._nodeDataService.getNodes().subscribe(
            // the first argument is a function which runs on success
            data => {
                this._nodeDataService.root = data;
                debugger;
                this._nodeDataService.root.x0 = 0;
                this._nodeDataService.root.y0 = this.height / 2;
                console.log("height", this.height);

                this.collapse = (d: any) => {
                    if (d.children) {
                        d._children = d.children;
                        d._children.forEach(this.collapse);
                        d.children = null;
                    }
                };
                this._nodeDataService.root.children.forEach(this.collapse);


                this.update(this._nodeDataService.root);
                // Call visit function to establish maxLabelLength
                this.visit(this._nodeDataService.root, (d: any) => {
                    let totalNodes: number = 0;
                    totalNodes++;
                    this.maxLabelLength = Math.max(d.name.length, this.maxLabelLength);

                }, (d: any) => {
                    return d.children && d.children.length > 0 ? d.children : null;
                });
            },
            // the second argument is a function which runs on error
            err => console.error(err),
            // the third argument is a function which runs on completion
            // () => console.log('done loading Nodes', this._nodeDataService.root)

        )
    };

    //necessary so that zoom knows where to zoom and unzoom from
    // zm.translate([350, 20]);

    selectFrame = d3.select(self.frameElement).style("height", "800px");

    update = (source: any) => {
        let i: number = 0;
        // Compute the new tree layout.
        let nodes = this.tree.nodes(this._nodeDataService.root).reverse(),
            links = this.tree.links(nodes);
        // Normalize for fixed-depth.
        nodes.forEach((n: any) => {
            n.y = n.depth * 180;
        });

        // Update the nodes…
        let node = this.svg.selectAll("g.node")
            .data(nodes, function (n: any) {
                return n.size || (n.id = ++i);
            }

            );

        // Enter any new nodes at the parent's previous position.
        let nodeEnter = node.enter().append("g")
            .attr("class", "node")
            .attr("transform", (n: any) => {

                return "translate(" + source.y0 + "," + source.x0 + ")";
            })

            .on("click", this.click);

        nodeEnter.append("rect")
            .attr("width", this.rectW)
            .attr("height", this.rectH)
            .attr("stroke", "black")
            .attr("stroke-width", 1)
            .style("fill", (d) => {
                return d._children ? "lightsteelblue" : "#fff";
            });

        nodeEnter.append("text")
            .attr("x", this.rectW / 2)
            .attr("y", this.rectH / 2)
            .attr("dy", ".35em")
            .attr("text-anchor", "middle")
            .text((d: any) => {
                return d.name;
            });

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

        nodeUpdate.select("rect")
            .attr("width", this.rectW)
            .attr("height", this.rectH)
            .attr("stroke", "black")
            .attr("stroke-width", 1)
            .style("fill", (d: any) => {
                return d._children ? "lightsteelblue" : "#fff";
            });

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

        // Transition exiting nodes to the parent's new position.
        let nodeExit = node.exit().transition()
            .duration(this.duration)
            .attr("transform", (n: any) => {
                return "translate(" + source.y + "," + source.x + ")";
            })
            .remove();

        nodeExit.select("rect")
            .attr("width", this.rectW)
            .attr("height", this.rectH)
            //.attr("width", bbox.getBBox().width)""
            //.attr("height", bbox.getBBox().height)
            .attr("stroke", "black")
            .attr("stroke-width", 1);

        nodeExit.select("text");

        // Update the links…
        var link = this.svg.selectAll("path.link")
            .data(links, function (n: any) {
                return n.target.id;
            });

        // Enter any new links at the parent's previous position.
        // Enter any new links at the parent's previous position.
        link.enter().insert("path", "g")
            .attr("class", "link")
            .attr("x", this.rectW / 2)
            .attr("y", this.rectH / 2)
            .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", (n: any) => {
                var o = {
                    x: source.x,
                    y: source.y
                }
                return this.diagonal({
                    source: o,
                    target: o
                });
            })

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

    };
    click = (d): void => {
        if (d.children) {
            d._children = d.children;
            d.children = null;
        } else {
            d.children = d._children;
            d._children = null;
        }
        this.update(d);
    };

    redraw = (d) => {
        this.svg.attr("transform",
            "translate(" + d3.event.translate + ")"
            + " scale(" + d3.event.scale + ")");
    }
}

Expected output attached as Image: Example

If anyone can help me out in this, it would be really a great help!!

Saravana
  • 524
  • 1
  • 6
  • 28
  • Can you provide a Plunker that allows to reproduce? Where is `update` being called from. – Günter Zöchbauer Dec 21 '16 at 06:31
  • try to update your code with ngAfterViewInit and ElementRef, instead of access the dom directly. http://stackoverflow.com/questions/37038467/how-to-implement-d3-in-angular-2 – Ravin Singh D Dec 22 '16 at 07:35
  • Where did you find this code? What does your data look like? Which tutorial are you following? – eko Dec 23 '16 at 04:45
  • @echonax i have been converting d3js tree code to typescript code.my data is actually a JSON data contains parent and children node for the D3 Tree. I will try to create a plunker and post it in a while.. – Saravana Dec 26 '16 at 04:59
  • @Sarav that'd be helpful – eko Dec 26 '16 at 05:00
  • here is my working plunker for the d3 typescript animation - https://plnkr.co/edit/cxuzCr?p=preview – Saravana Dec 27 '16 at 08:45
  • @Sarav so what's the problem here? – eko Dec 27 '16 at 09:35
  • yup, so, now instead of my static json data, I have to inject html contents. assume there are multiple steps. As and when I click on "finish" and "cancel" buttons respectively it should take me either to next node or to the previous node. – Saravana Dec 27 '16 at 10:21

0 Answers0