Struggling with D3.js Angular CLI where Scope disappears within the tick() function?

Hey everyone, I'm currently in the process of incorporating a D3 visualization network graph into an Angular CLI project (http://bl.ocks.org/mbostock/1153292) using the ng2-nvd3 component.

Here's the Angular component:

import { Component, OnInit } from '@angular/core';
declare let d3: any;

@Component({
  selector: 'app-visual',
  templateUrl: './visual.component.html',
  styleUrls: ['./visual.component.css']
})
export class VisualComponent implements OnInit {
   private links = [
    // List of link data
  ];
  private nodes: Array<Object> = [];
  private width = 960;
  private height = 500;
  private force: any;
  public svg: any;
  private path: any;
  private circle: any;
  private text: any;

  constructor(/*d3Service: D3Service*/) {
    // this.d3 = d3Service.getD3();
  }



ngOnInit() {
    // let d3 = this.d3;
    this.computeLinks(this.nodes);
    this.forceLayout();
    this.appendGraph();
  }

  computeLinks(nodes) {
    // Function to compute distinct nodes from links.
  }


  forceLayout() {
    // Function for force layout
  }

  appendGraph() {
    // Function to append graph elements
  }
  tick() {
    // Function to handle ticks
  }

  linkArc(d) {
    // Function for generating links between nodes
  }

  transform(d) {
    // Function for transforming node positions
  }
}

Error message

ERROR TypeError: Cannot read property 'attr' of undefined
at d3_dispatch.VisualComponent.tick (visual.component.ts:124)
at d3_dispatch.event [as tick] (d3.js:504)
at Object.force.tick [as c] (d3.js:6307)
at d3_timer_mark (d3.js:2166)
at d3_timer_step (d3.js:2147)
at ZoneDelegate.invokeTask (zone.js:425)
at Object.onInvokeTask (core.js:4747)
at ZoneDelegate.invokeTask (zone.js:424)
at Zone.runTask (zone.js:192)
at ZoneTask.invokeTask (zone.js:499)

Upon loading the web application, the graph is not visible: Snapshot of generated graph

Debugging by logging this.path in the tick() method returns undefined even though it was created in the appendGraph() method.

Answer №1

This is a different take on handling "setTimeout() inside JavaScript Class using “this”". In this scenario, D3's force layout relies on setTimeout() to manage its ticks, leading to issues with the scope of your tick() function when it runs.

You can see an example demonstrating this problem in action on JSFiddle.

In contrast to the previous question linked above, where you called setTimeout() in your own code, here D3 itself makes the call. This means utilizing a closure to maintain the necessary scope within your tick() method:

tick() {
  let self = this;     // capturing 'this' in a closure
  return function() {
    // 'self' retains reference to the class instance
    self.path.attr("d", self.linkArc);
    self.circle.attr("transform", self.transform);
    self.text.attr("transform", self.transform);
  }
}

In addition to these adjustments, you should ensure that the force layout initialization correctly invokes .tick() as a generator, returning the actual tick handler function that maintains the class instance's scope through this:

forceLayout() {
  this.force = d3.layout.force()
    //...details omitted for brevity
    .on("tick", this.tick())   // Note the parentheses after tick
    .start();
}

A fully functional demo showcasing these solutions can be found in this JSFiddle.

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

The issue of not being able to use res.flush as a function in Express-sse is

Currently, I am exploring the use of express-sse for the purpose of automatically triggering a notification and calling an API when an order is placed. Below is the code snippet for my API: router.post("/createOrder", async (req, res) => { try { ...

What is the method to display a caret in regular HTML text with the use of JavaScript?

Currently, I am studying JavaScript and aiming to develop a typing game similar to typeracer.com. The goal of the game is to type accurately and quickly without errors. In this game, users input text into a box, and JavaScript then compares their inputs w ...

Manipulate JSON insertions and character replacements using JavaScript or Python

I have a JSON file that contains an array of 500 objects. The structure of each object is as follows: { "books":[ { "title":"Title 1", "year":"2012", "authors":"Jack ; George", }, { "title":"Title 2", "year":" ...

Encountering a problem with Firebase while offline. The error message "FirebaseError: Firebase App named '[DEFAULT]' already exists with different options or config." is appearing

I've been having some trouble integrating Firebase into my app using the useFireBaseAuth hook. Everything works smoothly when there's an active internet connection, but I'm facing issues when offline. An error message shows up: Server Error ...

Debugging Node.js Routes in Visual Studio Code: A Step-by-Step Guide

My application is structured as follows: server.js router routes emails.js index.js In my index.js file, I define the route like this: module.exports = function (app) { app.use('/emails', require('./routes/emails& ...

The div has extra white space at the bottom due to the Hide/Show content feature

Having trouble stretching a scrolling div to 100% of its parent container's height? The Hide/Show content feature is causing whitespace at the bottom of the div. Check out the jsfiddle here: http://jsfiddle.net/fkvftff2/1/ LATEST UPDATE: The issue a ...

Add integer to an array of strings

Currently, I am utilizing an autocomplete feature and aiming to save the IDs of the selected users. My goal is to store these IDs in a string array, ensuring that all values are unique with no duplicates. I have attempted to push and convert the values u ...

Can Vue.js be paired with pure Node.js as a backend without relying on Express?

After successfully building a project with both vue js and node js, specifically with express, I'm curious if it's feasible to solely utilize node js without frameworks like express. ...

Can someone help me understand how to show nested values within an ng-repeat loop

How can I modify the code to only display rows with nested values? $scope.mainObj = [{ name: 'a', items : [1,2,3], name: 'b', items : [4,5,6], }] <table> <tr ng-repeat="obj in mainObj"> <td></td&g ...

Is it possible to edit YouTube images or embed YouTube iframes without needing an account?

Recently, I developed a YouTube video sharing system but have encountered some uncertainties. My approach involves extracting the YouTube ID and embedding it in an iframe (I wonder if YouTube permits this). To enhance the visual appeal of the posts, especi ...

I am encountering the ERR_STREAM_WRITE_AFTER_END error in my Node.js API. Does anyone know how to resolve this problem?

When I try to upload a file using the API from the UI, I encounter the following issue. I am interacting with a Node.js API from React.js and then making calls to a public API from the Node.js server. https://i.stack.imgur.com/2th8H.png Node version: 10. ...

Navigating through pages using Nuxt UI-pagination

Having some trouble with setting up the pagination feature from Nuxt UI. Despite trying to research through documentation and videos, I can't seem to figure out how to connect my data to the component. <template> <div> ...

Exploring ways to specifically select predefined strings by defining a regex parameter in angular-ui-router

I need to restrict access to a specific state in my angular-ui-router based on the parameter being one of the following strings: "site1" "site2" "site3" Here is my current state configuration: $stateProvider.state("error", { url: '/error/{sub: ...

Ensuring radio button validation prior to redirecting to contact form

I created a survey form using HTML and CSS. I am looking to add validation to the questions before redirecting to the Contact form page. Currently, when a user clicks on the submit button in the Survey form, the page redirects to the contact form. Howeve ...

What is the best way to utilize URL parameters to dynamically update a component's state within a class component

I am currently working on a React component that is making calls to two different API URLs, and the fetch operations are functioning as expected. However, I would like to utilize the URL handle structure, which looks like localhost://site/coins/[handle], a ...

Guide to activating the timer specifically on select pages with jQuery Mobile

I've developed a quiz application using jQuery Mobile and I am working on implementing a timer feature. The timer should run from 0 seconds up to 1 hour but only when the user is viewing specific pages, specifically the question pages. The timer is di ...

Troubleshooting: Why is $watch failing to track changes on factory variables in Angular

I have created a factory named SharedService as shown below: angular.module('Fms').factory('SharedService', function() { var userdetails=false; return userdetails; }) Below controllers utilize this factory: angular.mod ...

Trouble altering an attribute using jquery

Hey there, I'm struggling with changing the attribute for an id and can't quite pinpoint where I'm going wrong. It's not making things easier that I'm pretty new to this whole thing as well. I've created a function to ensure ...

TABULAOTR, The complete table calculation is failing to be retrieved

Apologies for any language mistakes, as I am Russian :)I am using Tabulator but facing an issue where the table footer is not being printed. I am also unable to retrieve data from the footer. The footer simply doesn't show up and I'm unsure of wh ...

Getting the total number of child elements in a web page using Selenium web-driver with Node.js

I've been looking everywhere, but I can't seem to find the answer Here's my HTML code: <form id="search_form_homepage" > ... <div class="search__autocomplete" style="display: block;"> &l ...