I'm facing challenges trying to implement the provided bl.ocks example in Typescript (using Angular).
This is my implementation in TypeScript: StackBlitz
Could anyone offer insights on what might be causing the issues I'm encountering? My initial guess points towards potential scoping inconsistencies with variables in typescript, despite ensuring proper casting. Any advice or suggestions would be greatly appreciated.
Bl.ocks example:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
color = d3.scaleOrdinal(d3.schemeCategory10);
var a = {id: "a"},
b = {id: "b"},
c = {id: "c"},
nodes = [a, b, c],
links = [];
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-1000))
.force("link", d3.forceLink(links).distance(200))
.force("x", d3.forceX())
.force("y", d3.forceY())
.alphaTarget(1)
.on("tick", ticked);
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + 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");
restart();
d3.timeout(function() {
links.push({source: a, target: b}); // Add a-b.
links.push({source: b, target: c}); // Add b-c.
links.push({source: c, target: a}); // Add c-a.
restart();
}, 1000);
d3.interval(function() {
nodes.pop(); // Remove c.
links.pop(); // Remove c-a.
links.pop(); // Remove b-c.
restart();
}, 2000, d3.now());
d3.interval(function() {
nodes.push(c); // Re-add c.
links.push({source: b, target: c}); // Re-add b-c.
links.push({source: c, target: a}); // Re-add c-a.
restart();
}, 2000, d3.now() + 1000);
function restart() {
// Apply the general update pattern to the nodes.
node = node.data(nodes, function(d) { return d.id;});
node.exit().remove();
node = node.enter().append("circle").attr("fill", function(d) { return color(d.id); }).attr("r", 8).merge(node);
// Apply the general update pattern to the links.
link = link.data(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(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
function ticked() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
My Implementation:
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;
constructor(private element: ElementRef) {
this.host = d3.select(this.element.nativeElement);
}
ngOnInit() {
this.buildViz();
}
buildViz() {
let svg = this.host.append('svg')
.attr('width', this.width)
.attr('height', this.height);
let color = d3.scaleOrdinal(d3.schemeCategory10);
var a = { id: "a" },
b = { id: "b" },
c = { id: "c" },
links = [];
var nodes = [{ id: "a" }, { id: "b" }, { id: "c" }];
var simulation = d3.forceSimulation<any>(nodes)
.force("charge", d3.forceManyBody().strength(-1000))
.force("link", d3.forceLink(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");
restart();
d3.timeout(function () {
links.push({ source: a, target: b }); // Add a-b.
links.push({ source: b, target: c }); // Add b-c.
links.push({ source: c, target: a }); // Add c-a.
restart();
}, 2000);
d3.interval(function () {
nodes.pop(); // Remove c.
links.pop(); // Remove c-a.
links.pop(); // Remove b-c.
restart();
}, 4000, d3.now());
d3.interval(function () {
nodes.push(c); // Re-add c.
links.push({ source: b, target: c }); // Re-add b-c.
links.push({ source: c, target: a }); // Re-add c-a.
restart();
}, 2000, d3.now() + 1000);
function restart() {
// Apply the general update pattern to the nodes.
node = node.data(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(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(nodes);
simulation.force<any>("link").links(links);
simulation.alpha(1).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; });
}
}
}