Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3-ng2-service error "property Links does not exists of type force()"

Tags:

angular

d3.js

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;
    }

  }

}
like image 240
Paul Townend Avatar asked Dec 19 '22 09:12

Paul Townend


1 Answers

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.

like image 180
tomwanzek Avatar answered Apr 30 '23 04:04

tomwanzek