I have just started using Angularjs 2 and d3-ng2-service and I am trying to get a network chart working.
I use example
https://bl.ocks.org/mbostock/2675ff61ea5e063ede2b5d63c08020c7
and transitioning it to d3-ng2-service.
I am getting the following error
"property Links does not exists of type force()"
With the following code
simulation.force("link")
.links(links);
Full code listed below
import { Component, ElementRef, NgZone, OnDestroy, OnInit } from '@angular/core';
import { links, nodes} from './networkData.component';
import {
D3Service,
D3,
Axis,
ScaleLinear,
ScaleOrdinal,
Selection,
Simulation,
Transition
} from 'd3-ng2-service';
@Component({
selector: 'network1',
template: '<svg width="100%" height="100%"></svg>',
styleUrls: ['./network.component.css']
})
export class Network1Component implements OnInit, OnDestroy {
private d3: D3;
private parentNativeElement: any;
private d3Svg: Selection<SVGSVGElement, any, null, undefined>;
constructor(element: ElementRef, private ngZone: NgZone, d3Service: D3Service) {
this.d3 = d3Service.getD3();
this.parentNativeElement = element.nativeElement;
}
ngOnDestroy() {
if (this.d3Svg.empty && !this.d3Svg.empty()) {
this.d3Svg.selectAll('*').remove();
}
}
ngOnInit() {
let self = this;
let d3 = this.d3;
let width: number;
let height: number;
let d3ParentElement: Selection<HTMLElement, any, null, undefined>;
let d3Svg: Selection<SVGSVGElement, any, null, undefined>;
if (this.parentNativeElement !== null) {
d3ParentElement = d3.select(this.parentNativeElement);
d3Svg = this.d3Svg = d3ParentElement.select<SVGSVGElement>('svg');
width = +d3Svg.attr('width');
height = +d3Svg.attr('height');
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d[0].id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = d3Svg.append<SVGGElement>('g')
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = d3Svg.append<SVGGElement>('g')
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.id); });
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
function ticked() {
link
.attr("x1", function(d) { return d.source; })
.attr("y1", function(d) { return d.source; })
.attr("x2", function(d) { return d.target; })
.attr("y2", function(d) { return d.target; });
node
.attr("cx", function(d) { return d.id; })
.attr("cy", function(d) { return d.id; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
}
In porting the bl.ock to TypeScript, there are a couple of tweaks required to ensure the data types of nodes and links and the varying force types are handled correctly.
forceSimulation
accepts two generics when invoking it. Let us assume, your nodes are of type YourNodeType
which extends the SimulationNodeDatum
and links are of type YourLinkType
which extends SimulationLinkDatum<YourNodeType>
.SimulationNodeDatum
and SimulationLinkDatum
are interfaces defined in the d3-force definitions.
(see d3-force definition).
With that said, you should define let simulation = d3.forceSimulation<YourNodeType, YourLinkType>()
This way, node and for your case importantly link types are enforced throughout the simulation.
Now to the core of the error you experience, in order to retrieve a force with properties other than the minimal Force
interface,it is necessary to cast the force to the ex ante known type. In your case the link force has a pre-defined interface in the definitions.
So simulation.force<ForceLink<YourNodeType, YourLinkType>>('link')
, will return a link force with defined links()
method.
A couple of housekeeping comments, since you are using d3-ng2-service, the interfaces SimulationNodeDatum
, SimulationLinkDatum
and ForceLink
can be imported directly from d3-ng2-service
into the file containing your angular component.
If you are using strictNullChecks
, you may want to simulation.force<ForceLink<YourNodeType, YourLinkType>>('link')!.links()
, where !
addresses the case where the returned force might be undefined
in general. Not the case for you, obviously.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With