Dealing with Scoping Problems in a Typescript d3 Update Tutorial

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

}

Explore D3 Forced Layout Example

Answer №1

After examining your code, I have identified two mistakes that need to be corrected:

1) The code snippet below creates an array of nodes that lose reference to the original a, b, and c objects:

var a = { id: "a" },
  b = { id: "b" },
  c = { id: "c" },
  links = [];

var nodes = [{ id: "a" }, { id: "b" }, { id: "c" }];

To maintain the same reference to the original objects in the array, it should be written as:

var nodes = [a, b, c];

2) There are incorrect delay values set:

d3.interval(function () {
  nodes.pop(); // Remove c.
  links.pop(); // Remove c-a.
  links.pop(); // Remove b-c.
  restart();
}, 4000, d3.now());
   ^^^^^
 This should be changed to 2000 as per the original example

Forked Stackblitz Example

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Discovering JSON data in Angular 4

I am working with a JSON data format retrieved from (this.url) { "user": [ { "id": "1", "name": "root", "password": "root" }, { "id": "2", "name": "clienttest", ...

The service that offers an Observable on a specific subject is not receiving any notifications

The EventSpinner component is designed to subscribe to icons provided by the EventsService. @Component({ selector: 'event-spinner', template: ` <div class="col-xs-5"> Test <i class="fa fa-2x" [ngClass]="{'fa-check' ...

The Angular Date Pipe is currently unable to display solely the Month and Year; it instead presents the complete date, including day, month,

I'm currently using the Bootstrap input date feature to save a new Date from a dialog box. However, I only want to display the month and year when showing the date. Even though I added a pipe to show just the month and year, it still displays the mont ...

Protractor Browser Instance Failure

We have encountered an issue with our UI suite failing in Chrome during the login process. Initially, we thought it might be due to upgrading to Chrome 79, as the problems arose simultaneously. Interestingly, the login functionality still works smoothly in ...

Retrieve the interface property following the execution of the HTTP GET service

Having trouble accessing the array properties from my JSON file using the http get service. The goal is to display the Widget array on the web. service.ts: import { Http, Response, Headers } from '@angular/http'; import { Observable } from &apo ...

Run a single feature file in Protractor by utilizing a specific tag

Is it possible to run just one feature file in Protractor? I am aware that I can specify the file in protractor.conf.js, but I have come across a different solution involving the use of a tag: To single out a specific feature file, you would include a tag ...

Issue: NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper]:

While I am going through a tutorial on the abp framework, I encountered an error with the Author route that says "ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelp ...

The type 'number[]' is lacking the properties 0, 1, 2, and 3 found in the type '[number, number, number, number]'

type spacing = [number, number, number, number] interface ISpacingProps { defaultValue?: spacing className?: string disabled?: boolean min?: number max?: number onChange?: (value: number | string) => void } interface IFieldState { value: ...

a dedicated TypeScript interface for a particular JSON schema

I am pondering, how can I generate a TypeScript interface for JSON data like this: "Cities": { "NY": ["New York", [8000, 134]], "LA": ["Los Angeles", [4000, 97]], } I'm uncertain about how to handle these nested arrays and u ...

Angular navigate to query in deferred loading module

I have lazy loaded routes set up like this: const routes: Routes = [ { path: ':lang', loadChildren: './components/home/home.module#HomeModule', // redirectTo: "en" }, { path: ':id/custom ...

Using type definitions in non-TS files with VSCode: A beginner's guide

My code is not in TypeScript, shown here: // foo.js module.exports = app => { // some logic here } I want to enhance my development experience by using TypeScript definition files to specify the type of the argument app, enabling VSCode to provide ...

Angular: Struggling with Parent Component not recognizing changes in Child Component

Currently, I am facing an issue with my shopping cart functionality. When the website first loads and a user does not have a saved shopping cart, they need to click "add to cart" before one is created. The problem lies in the fact that my app.component doe ...

What is the best method for consistently populating data in an Angular application?

After successfully retrieving data from the backend and displaying it on the frontend in my application, I encountered a scenario where sometimes the data is not visible when navigating from other pages. A manual refresh of the page is required to display ...

Global installation of Node modules

How do I reference globally installed node modules? For example, if I have a package.json file and I choose to install all the node modules listed in it globally (located at C:\Users\MyaccountName\AppData\Roaming\npm), how can I ac ...

Setting up conditional select options in Angular

I need to create a select list with specific options based on the data in an object. For instance, if the data property is 0, I want to display an option as 0 with the ability to switch to 1, and vice versa. However, when rendered in HTML, no option value ...

Using Typescript to combine strings with the newline character

Currently, I am delving into Angular2 and facing the challenge of creating a new line for my dynamically generated string. For example: input: Hello how are you ? output: Hello how are you? Below is the code snippet: .html <div class="row"> ...

React: Switching PopUp causes the entire component to be re-rendered

Currently, I am in the process of familiarizing myself with React, so I appreciate your patience. I am developing a component using MaterialUI which consists of a grid and a PopOver. A basic mockup of this component is as follows: export const Overview ...

What is the solution for fixing the Typescript error in formik onSubmit?

I encountered an error while using the onSubmit prop of Formik: The type '(values: { email: string; password: string; }) => { type: string; payload: { email: string | null; password: string | null; }; }' is not compatible with the type '(( ...

Executing a single insert statement in a TypeScript Express application using PostgreSQL is taking over 240 milliseconds to complete

Seeking assistance with optimizing a db insert operation test using mocha for a node.js express app that utilizes fp-ts and pg npm package. The tests run successfully, but the insert test is taking over 240 ms to complete. The database table has a maximum ...

The final value of a loop is consistently returned each time

Creating a loop to generate objects. Action.ts public campaing:any = { 'id': '', 'campaing_code': '', 'campaing_type': '', 'start_date': this.currentDate, 'end ...