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

When using Angularfire, the function to switch the type from snapshotChanges will consistently return the value as "value"

At this moment, when I use the Angularfire extension to call the following code: this.db.doc(path).snapshotChanges(); Angularfire always retrieves a DocumentSnapshot with a type that is consistently "value", regardless of the actual change type. Is there ...

I'm having trouble getting my Ionic Angular App to start up properly. When I try to launch it, it just gets stuck on the splash screen and displays an error message saying "ReferenceError

The challenge I am facing involves developing an IOS app using ionic v7 and angular v15.2. While the app functions smoothly in the browser, upon deployment to my iPhone (iphone 13 pro, running IOS 15.6), I encounter a perplexing error message within XCode: ...

Resolve the issue of text overlapping on an image when zooming in

There seems to be an issue with overlapping text and images when zooming the page. I have included a screenshot for reference, please take a look and provide a solution. Thank you in advance. Here is the CSS code causing the overlap: .Pad{ padding: 6 ...

What is the best way to clear a token from SessionStorage upon exiting an Angular application?

I need to clear my sessionStorage every time I exit my application. App Module: export class AppModule implements OnInit, OnDestroy{ constructor(private overlayService: OverlayService, private logger: LoggerService, private userService: UserService, pr ...

Can an Angular application be developed without the use of the app-root tag in the index.html file?

I'm a newcomer to Angular and I'm trying to wrap my head around how it functions. For example, if I have a component named "login", how can I make Angular render it directly? When I try to replace the app-root tag with app-login in index.html, n ...

Using Angular frontend to access Django Admin

Is it possible to integrate Django admin with an Angular frontend? I'm currently using Angular version 8.0 for the frontend and Django for the backend. In my urls.py file, I have added the admin as shown below: from django.urls import path, re_path f ...

Utilizing TypeScript code to access updatedAt timestamps in Mongoose

When querying the database, I receive the document type as a return. const table: TableDocument = await this.tableSchema.create({ ...createTableDto }) console.log(table) The structure of the table object is as follows: { createdBy: '12', cap ...

Restrict the number of items in a list in Angular 2 to a

I need help structuring a list of material cards in my application. The list can potentially have a large number of elements, so I want to limit it to showing only 5 items at a time and provide an option for the user to navigate through additional elements ...

What is the process of sending an HTTP post request in Angular 4 Universal?

Currently, I am working with Angular 4 Universal and attempting to send emails through a contact form using nodemailer. However, I am encountering issues with posting data via http.post. Contact Template HTML: <form (ngSubmit)="onSubmit()"> & ...

Guide on deploying an Angular 7 frontend paired with a Spring Boot backend

Having trouble with integrating my Angular application with Spring Boot REST backend. Can anyone suggest a simple method to run both on the same localhost port? ...

Sending a parameter to a route guard

I've been developing an application that involves multiple roles, each requiring its own guard to restrict access to various parts of the app. While I know it's possible to create separate guard classes for each role, I'm hoping to find a mo ...

Establishing a default value for an ion-datetime in Ionic 4

I am working on creating an edit form and need to specify a default date in the ion-datetime field. In our .html file: <ion-datetime #fecha displayFormat="DD/MM/YYYY" [(ngModel)]="myVar" pickerFormat="DD MM YYYY" doneText="Ok" cancelText="Cancelar"> ...

Enhancing the appearance of the Mui v5 AppBar with personalized styles

I am encountering an issue when trying to apply custom styles to the Mui v5 AppBar component in Typescript. import { alpha } from '@mui/material/styles'; export function bgBlur(props: { color: any; blur?: any; opacity?: any; imgUrl?: any; }) { ...

"Time" for creating a date with just the year or the month and year

I am trying to convert a date string from the format "YYYYMMDD" to a different format using moment.js. Here is the code snippet I am using: import moment from 'moment'; getDateStr(date: string, format){ return moment(date, 'YYYYMMDD&a ...

Steps for excluding a file from compilation during angular testingExclude a file from being

I have a dilemma with my Angular 7 application that I built using the angular cli. During the ng-test process, I need a specific file to be excluded from compilation. The file causing issues is "app/config/context.prod.ts" To rectify this, I included th ...

Using WebdriverIO with Angular to create end-to-end tests in TypeScript that involve importing classes leads to an error stating "Cannot use import statement outside a module."

I am facing an issue while trying to set up a suite of end-to-end tests using wdio. Some of the tests utilize utility classes written in TypeScript. During the compilation of the test, I encountered the following error: Spec file(s): D:\TEMP\xx& ...

Angular: Dynamically Displaying Components based on Conditions

I have a unique component design: <div class="custom-component__section {{ style }}"> <header class="custom-component__header"> <ng-content *ngIf="!displayHeader" select="[custom-header]">< ...

Angular4 ASP.NET Core project does not seem to be detecting the API endpoints

I am encountering an issue with my angular application where I consistently receive a "Cannot match any routes" error when attempting to access certain api resources. The base in my application is configured as <base href="/"> and the api source is ...

The Step-by-Step Guide to Deselecting an Angular Checkbox with a Button

I currently have a situation where I have three checkboxes labeled as parent 1, parent 2, and parent 3. Initially, when the page loads, parent 1 and parent 3 are checked by default, while parent 2 is unchecked. However, when I manually check parent 2 and c ...

Issue with reflect metadata in Next.js edge runtime causing functional problems

Currently, I am utilizing a package in my upcoming 13 app that incorporates reflect metadata. Unfortunately, during the next build process, an error occurs for which I haven't been able to find a solution. ../../eshop-sdk-js/node_modules/reflect-metad ...