'd3 drag: Cannot create property 'fx' on number '0'

first of all thanks for entering the question.

My issue is my drag handler is not working as expected on d3 v6/v7 (tried both). Right now I'm not receiving the event in the function, so I can't assign the new x,y values to the node.

Here is my node structure:

{
      "id": string,
      "color": string,
      "distance": number,
      "size": number,
      "type": string,
      "visibility": string,
      "data": any[]
},

Here is my line structure:

{
      "source": string,
      "target": string,
      "distance": number,
      "size": number,
      "color": string,
      "visibility": string,
      "type": string
    }

Here is my code:

import { Component, OnInit } from '@angular/core';
import { select, selectAll } from 'd3-selection';
import { transition } from 'd3-transition';
import { forceSimulation, forceLink, forceManyBody, forceCenter } from 'd3-force';
import { drag } from 'd3-drag';
const d3 = { select, selectAll, transition, forceSimulation, forceLink, forceManyBody, forceCenter, drag };

import { S4pService } from 'src/app/services/s4p.service';
import { Router } from '@angular/router';


@Component({
  selector: 'app-mapit',
  templateUrl: './mapit.component.html',
  styleUrls: []
})
export class MapitComponent implements OnInit {

  svg_width = 1252;
  svg_height = 601;
  radius = 10;

  NODES: any;
  LINKS: any;

  constructor(private s4p: S4pService, private router: Router){}

  ngOnInit(): void {
    this.s4p.getNetworkData(this.router.url).subscribe((data: any) => {
      this.NODES = data.nodes;
      this.LINKS = data.links;
      this.createGraph();
    });
    
  }

  createGraph(){

    const svg = d3.select('#container');

    const svg_container = document.getElementById('map-container')!;
    this.svg_width = svg_container.offsetWidth;
    this.svg_height = svg_container.offsetHeight;
    const centerX = this.svg_width/2;
    const centerY = this.svg_height/2;

    const simulation = d3.forceSimulation(this.NODES)
      .force('charge', d3.forceManyBody().strength(-20))
      .force('link', d3.forceLink(this.LINKS).id((d:any) => d.id ).distance(((link: any) => link.distance) as any))
      .force('center', d3.forceCenter(centerX, centerY));
    
    const dragInteraction: any = d3.drag().on('drag', (event: any, node: any) => {
      console.log(event);     // --> console.log() added to question below
      console.log(node);      // --> console.log() added to question below
      node.fx = event.x;
      node.fy = event.y;
      simulation.alpha(1);
      simulation.restart();
    });

    const lines = svg
      .selectAll('line')
      .data(this.LINKS)
      .enter()
      .append('line')
      .attr('stroke', ((link: any) => link.color || 'black') as any)
      .attr('visibility', (link: any) => link.source.type === 'Main' && link.target.type === 'Main' ? 'visible !important' : link.visibility )
      .attr('class', (link: any) => `line-${ link.source.id }`)
      .attr('source', (link: any) => link.source.id )
      .attr('target', (link: any) => link.target.id )
      .attr('type', 'line')

    const circles = svg
      .selectAll('circle')
      .data(this.NODES)
      .enter()
      .append('circle')
      .attr('fill', ((node: any) => node.color || 'gray') as any)
      .attr('r', this.radius - .75 /*((node: any) => node.size) as any*/)
      .attr('class', (node: any) => `node-${ node.id }`)
      .attr('id', (node: any) => `node-${ node.id }`)
      .attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
      .attr('parents', (node: any) => node.parents ? node.parents : '')
      .call(dragInteraction)
      .on("mouseover", mouseover)
      .on("mouseout", (node:any) => mouseout(node))
      .on("contextmenu", (node:any) =>  alert(JSON.stringify(node.srcElement.__data__)))
      .on("click", (n:any) => displayChildNodes(n));
    
    const text = svg
      .selectAll('text')
      .data(this.NODES)
      .enter()
      .append('text')
      .attr('text-anchor', ('middle') as any)
      .attr('alignment-baseline', ('middle') as any)
      .attr('class', (node: any) => `text-${ node.id }`)
      .attr('id', (node: any) => `text-${ node.id }`)
      .attr('visibility', (node: any) => node.visibility ? node.visibility : 'hidden')
      .attr('type', 'text')
      .style('pointer-events', ('none') as any)
      .text( (node: any) => node.id );
    
    simulation.on('tick', () => {
      let width = this.svg_width;
      let height = this.svg_height;
        circles
        .attr("cx", function(d: any) { return d.x = Math.max(10, Math.min(width - 10, d.x)); })
        .attr("cy", function(d: any) { return d.y = Math.max(10, Math.min(height - 10, d.y)); });
        
        text
          .attr('x', ((node: any) => node.x) as any)
          .attr('y', ((node: any) => node.y) as any);
   
        lines
          .attr('x1', ((link: any) => link.source.x) as any)
          .attr('y1', ((link: any) => link.source.y) as any)
          .attr('x2', ((link: any) => link.target.x) as any)
          .attr('y2', ((link: any) => link.target.y) as any)
      });

    function mouseover(d: any) {
        lines
          .attr("stroke","black")
          .attr("stroke-width",2)

        d3.selectAll('line')
          .transition()
          .duration(500)
          .style("opacity", function(o: any) {
              return o.source.id === d.id || o.target.id === d.id ? 1 : .1;
          });

        circles
          .transition()
          .duration(500)
          .attr('r', (node:any) => {
            if(node.id === d.id) return node.size + 15
            return node.size;
          });
    }

    function mouseout(d: any) { 
      lines
        .attr('stroke', ((line: any) => line.color) as any );
      lines
        .transition()
        .duration(500)
        .style("opacity", 1);
      circles
        .attr('fill', ((node: any) => node.color || 'gray') as any)
        .attr('r', 10) /*((node: any) => node.size) as any)*/
      circles
        .transition()
        .duration(500)
     }


    function displayChildNodes(node: any){
      const node_lines: any = document.getElementsByClassName(`line-${ node.id }`);
      for(let line of node_lines){
        const childNode = document.getElementById(`node-${ line.getAttribute('target')}`)!;
        const childNodeText = document.getElementById(`text-${ line.getAttribute('target')}`)!;
        childNode.getAttribute('visibility') === 'visible' ? childNode.setAttribute('visibility', 'hidden') : childNode.setAttribute('visibility', 'visible');
        childNodeText.getAttribute('visibility') === 'visible' ? childNodeText.setAttribute('visibility', 'hidden') : childNodeText.setAttribute('visibility', 'visible');
        
        line.getAttribute('visibility') === 'visible' ? line.setAttribute('visibility', 'hidden') : line.setAttribute('visibility', 'visible');

        let other_parents = childNode.getAttribute('parents')!.split('*');
        other_parents = [...new Set(other_parents)];
        let myindex = other_parents.indexOf('undefined');
        other_parents.splice(myindex, 1);
        myindex = other_parents.indexOf(line.source);
        other_parents.splice(myindex, 1);
        
        for(let parent_id of other_parents){
          const parent_lines = document.getElementsByClassName(`line-${ parent_id }`)!;
          for(let i=0; i<parent_lines.length; i++){
            const ids = childNode.getAttribute('id')!.split('-');
            if(parent_lines[i].getAttribute('target') === ids[1] && parent_lines[i].getAttribute('visibility') === 'visible'){
              childNode.setAttribute('visibility', 'visible');
              childNodeText.setAttribute('visibility', 'visible')
            } 
          }
        } 
      }
    }
  }


}

Here the console.log()s at drag handler:

For event (no event received, instead I'm receiving the node object):

{
  "id": "myid",
  "color": "#CBE4F9",
  "distance": 5,
  "size": 50,
  "type": "Main",
  "visibility": "visible",
  "index": 0,
  "x": 709.282818224574,
  "y": 98.26132198282302,
  "vy": -0.0306124925872071,
  "vx": -0.02388584994074383
}

For node (no node received, instead, I'm receiving a number which corresponds to the index position of the node in nodes array):

0

Just after the console.logs I receive the error:

enter image description here

Has anyone been facing a similar issue? Any help is welcomed.

Thanks in advice.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source