Angular 6 and D3 version 5.5 are causing an issue with the undefined `<variable>`

At the moment, I am attempting to create a Hierarchical Bar Chart in my Angular App using D3. When I click on a bar, I expect my function to recursively reshape the chart. The initial call works fine, but once I click on a bar, the variables become undefined.

After clicking on a bar, the updateChart function is triggered. However, this leads to an error being displayed in the console:

Error on bar click

I attempted to store the variables in an object and import them. I also tried to assign them a value in the constructor, following the example I found here: Typescript: getting an undefined value , but nothing seems to resolve the issue.

The code I'm working with is based on a variation of this plunker: https://stackblitz.com/edit/angular-d3-v4-barchart?file=app%2Fshared%2Fbarchart%2Fbarchart.component.ts

As a newcomer to this, my code likely contains numerous mistakes, but the main hurdle I'm facing is utilizing the variables in the updateChart function.

import { Component, OnInit, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';
import { IHierarchyDatum } from './IHierarchyDatum';
import { HierarchyNode } from 'd3-hierarchy';
import { Agp } from '../agp';

@Component({
  selector: 'app-d3-agp',
  templateUrl: './d3-agp.component.html',
  styleUrls: ['./d3-agp.component.css'],
  encapsulation: ViewEncapsulation.None
})

export class D3AgpComponent implements OnInit {

  @ViewChild('chart') private chartContainer: ElementRef;
  private margin: any = { top: 20, bottom: 20, left: 20, right: 20 };
  private chart: any;
  private width: number;
  private height: number;
  private xScale: any;
  private yScale: any;
  private colors: any;
  private xAxis: any;
  private yAxis: any;
  help: IHierarchyDatum;

  constructor (public agp: Agp) {
  }

  ngOnInit () {
    d3.json('http://localhost:3000/posts')
      .then((data: IHierarchyDatum) => {
        if (data) {
          this.help = data;
          this.createChart();
        }
      })
      .catch((err) => {
        console.log(err);
      });
  }

  createChart () {
    let root = d3.hierarchy(this.help[0], function (d) {
      return d.children;
    });
    root = root.sum(function (d) {
      return d.size;
    });
    const element = this.chartContainer.nativeElement;
    this.width = element.offsetWidth - this.margin.left - this.margin.right;
    this.height = element.offsetHeight - this.margin.top - this.margin.bottom;
    const svg = d3.select(element).append('svg')
      .attr('width', element.offsetWidth)
      .attr('height', element.offsetHeight);

    this.chart = svg.append('g')
      .attr('class', 'bars')
      .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
    const xDomain = root.children.map(d => d.data.name);
    const yDomain = [0, d3.max(root.children, d => d.value)];

    this.xScale = d3.scaleBand().padding(0.1).domain(xDomain).rangeRound([0, this.width]);
    this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);

    this.colors = d3.scaleLinear().domain([0, root.children.length]).range(<any[]>['red', 'blue']);

    this.xAxis = svg.append('g')
      .attr('class', 'xAxis')
      .attr('transform', `translate(${this.margin.left}, ${this.margin.top + this.height})`)
      .call(d3.axisBottom(this.xScale));

    this.yAxis = svg.append('g')
      .attr('class', 'yAxis')
      .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
      .call(d3.axisRight(this.yScale));

    this.updateChart(root);
  }

  updateChart (root: HierarchyNode<IHierarchyDatum>) {

    this.xScale.domain(root.children.map(d => d.data.name));
    this.yScale.domain([0, d3.max(root.children, d => d.value)]);
    this.colors.domain([0, root.children.length]);
    this.xAxis.transition().call(d3.axisBottom(this.xScale));
    this.yAxis.transition().call(d3.axisLeft(this.yScale));

    const update = this.chart.selectAll('.bar')
      .data(root.children)
      .enter()
      .append('rect')
      .attr('class', 'bar')
      .attr('x', d => this.xScale(d.data.name))
      .attr('y', d => this.yScale(0))
      .attr('width', this.xScale.bandwidth())
      .attr('height', 0)
      .style('fill', (d, i) => this.colors(i))
      .transition()
      .delay((d, i) => i * 10)
      .attr('y', d => this.yScale(d.value))
      .attr('height', d => this.height - this.yScale(d.value));

    this.chart.selectAll('rect')
      .on('click', this.updateChart);
  }
}

Answer №1

Dealing with a similar issue in my D3 code using angular

When binding a function in D3, the context inside the event handler becomes the object triggering the event (such as a rectangle in the case of a mouseover on a rectangle). To maintain your angular context, you need to bind your function like this

// your code snippet here
  this.chart.selectAll('rect')
  .on('click', this.updateChart);
  } // end of the function creating your element

public updateChart = () => {
   //code to update the chart
}

Hope this provides some assistance

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

Executing a method during the initialization process in app.component.ts

One thing I've noticed is that the <app-root> component in Angular doesn't implement OnInit like all the other components. It may sound silly, but let's say I wanted to add a simple console.log('Hello World') statement to dis ...

Verify whether the default export of a file is a React function component or a standard function

Trying to figure out how to distinguish between modules exporting React function components and regular functions. Bun employs file-based routing, enabling me to match requests with module files to dynamically import based on the request pathname. Conside ...

Is there a method to incorporate absolute paths in SCSS while working with Vite?

Currently, I am experimenting with React + Vite as webpack seems to be sluggish for me. My goal is to create a project starter, but I am facing difficulties in getting SCSS files to use absolute paths. Despite including vite-tsconfig-paths in my vite.confi ...

Exploring Angular Unit Testing: A Beginner's Guide to Running a Simple Test

I'm diving into the world of angular unit testing and looking to set up my first successful test. Here's what I've come up with: import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { AppComponent } fro ...

Inversify: class-based contextual dependency injection

I am currently experimenting with injecting loggers into various classes using inversify. My goal is to pass the target class name to the logger for categorization. The challenge I'm facing is the inability to access the target name from where I am c ...

Implementing a unit test in Angular for an HTTP interceptor that adds an XCSRF token to the header

My current task involves unit testing a global HTTP interceptor that retrieves the XCSRF token from a service call getResponse() and appends it to the header only in POST requests using the postResponse() method (as described below). I have attempted to t ...

Issue: Angular Application experiencing failure during NGCC operation execution

As I work on my Angular Application in VS Code, I keep encountering popups when opening the application. Despite this, the 'ng serve' command functions properly and successfully runs the app. However, I am noticing an error in the console with t ...

“What is the process of setting a referenced object to null?”

Here is an example of the code I'm working with: ngOnInit{ let p1 : Person = {}; console.log(p1); //Object { } this.setNull<Person>(p1); console.log(p1); //Object { } } private setNull<T>(obj : T){ obj = null; } My objective is to ...

What is the method for updating a 'Signal' within an 'Effect'?

Working with signals in Angular 17, I encountered an issue while trying to update the value of a signal. The error message that I received is as follows: NG0600: Writing to signals is not allowed in a `computed` or an `effect` by default. Use `allowSignalW ...

Encountering error TS2307: Module 'redux' not found when trying to implement redux in Angular 7

Currently, I am diving into the world of Redux and attempting to integrate it into my Angular 7 project using ng2-redux. However, upon visiting the npm page, I discovered that the recommended approach was to install it with npm install @angular-redux/store ...

Incorporating a parameter into a <div> only when the parameter holds a value

Being new to web development, I have a rather basic query. If the datainfo prop variable is not empty, I'd like to include a data attribute in the div tag. <div style={{height: props.height - handleHeight()}} data={datainfo ? datainfo : ""}> C ...

Is it possible to integrate the extension library of three.js (including OBJLoader, SceneUtils, etc.) with Angular 6?

Attempting to implement the below code, however, it is not functioning as expected. npm install --save three-obj-loader import * as ThreeObjLoader from 'three-obj-loader'; const OBJLoader = ThreeObjLoader(THREE); let loader = new OBJLoader(): ...

How can I get video playback in IOS to work with Videogular2 using HLS?

I recently integrated videogular2 into my Angular 6 app to display HLS streams. Everything seems to be working smoothly on desktop and Android devices, but I encountered an error when testing on IOS: TypeError: undefined is not an object (evaluating &apos ...

Disallow negative numbers but allow decimals in HTML input

I need help restricting user input to prevent negative numbers while still allowing floating point numbers in my Angular project. Any suggestions using Angular tools would be greatly appreciated. Thanks! ...

Troubleshooting Issue with Accessing ASP.NET Core WebApi through Ionic

Having trouble making a connection between my ASP.NET Core WebAPI and Ionic application. The data seems to be loading correctly based on the developer tools, but an error is thrown by Ionic: Error message from Ionic: https://i.sstatic.net/CXroV.png Here ...

Implementing dynamic classes for each level of ul li using JavaScript

Can anyone help me achieve the goal of assigning different classes to each ul li element in vanilla Javascript? I have attempted a solution using jQuery, but I need it done without using any libraries. Any assistance would be greatly appreciated! con ...

Proper utilization of react-hook-form in conjunction with TypeScript and material-ui to display error messages efficiently

Currently, I am using a combination of react-hook-form with typescript and material-ui. My goal is to pass the error message as a helperText to the TextField. I attempted to achieve this by utilizing helperText={errors.email?.message} however, TypeScript ...

What steps can I take to troubleshoot the issue with ionicons not functioning properly?

After starting to work on my app with Ionic 2, the ionicons were displaying properly. But now, for some reason, they are not showing up at all. Do you have any idea why this might be happening? Is it necessary to add a specific library or something similar ...

Issues have been reported with Angular 10's router and anchorScrolling feature when used within a div that is positioned absolutely and has overflow set

I feel like I may be doing something incorrectly, but I can't quite pinpoint the issue. Any help or pointers would be greatly appreciated. My current setup involves Angular 10 and I have activated anchorScrolling in the app-routing.module file. const ...

What is the most effective way to inform TypeScript that my function will return a class that has been expanded by a specific class?

Imagine a scenario where we have the following classes: class A { constructor($elem: JQuery<HTMLElement>) { $elem.data('plugin', this); } inheritedMethod() { ... } } class B extends A { constructor($ele ...