Horizontal scroll functionality featured in a D3 bar graph

I'm currently working on implementing a bar graph with a selection dropdown that includes 3 values: By Date, By Week, By Month (where 'By Date' is the default option).

When retrieving data from the backend for 'ByDate', I have a large number of data points (around 50). As a result, when plotting these points, the x-axis becomes crowded and it's difficult to apply horizontal scrolling.

I attempted to create a demo on Stackblitz, but encountered a dependency issue that I couldn't resolve. However, you can view all the code here: Link

I was aiming for the bar graph to look something like this: Example

Apologies for not providing a functional demo, but I would appreciate any guidance on this matter.

Here is my function:

createGraphData() {
    const margin = { top: 20, right: 30, bottom: 50, left: 60 };
    const width = 800 - margin.left - margin.right;
    const height = 400 - margin.top - margin.bottom;

    d3.select('#bar-chart').select('svg').remove(); // Remove existing chart

    const svg = d3
      .select('#bar-chart')
      .append('svg')
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    // Choose the appropriate dataset based on the selected option
    const dataset =
      this.graphRespId === 1
        ? this.graphDetails.lstByDateList
        : this.graphRespId === 2
          ? this.graphDetails.lstByWeekDateList
          : this.graphDetails.lstByMonthList;

    // Extract x-axis and y-axis values from the dataset
    const xValues = this.getXValues(dataset);
    const yValues = dataset.map((data) => data.CountName);
    // Set up x-axis and y-axis scales
    const xScale = d3.scaleBand().domain(xValues).range([0, width]).padding(0.1);
    const yScale = d3.scaleLinear().domain([0, Math.ceil(d3.max(yValues) / 10) * 10 + 5]).range([height, 0]);

    // Draw x-axis
    svg.append('g')
      .attr('transform', 'translate(0,' + height + ')')
      .call(d3.axisBottom(xScale));

    // Draw y-axis
    svg.append('g').call(d3.axisLeft(yScale));

    // Draw bars
    svg.selectAll('rect')
      .data(dataset)
      .enter()
      .append('rect')
      .attr('x', (data) => xScale(data[this.getXAxisKey()]))
      .attr('y', (data) => yScale(data.CountName))
      .attr('width', xScale.bandwidth())
      .attr('height', (data) => height - yScale(data.CountName))
      .attr('fill', () => this.getRandomColor());
  }

getXAxisKey() {
    return this.graphRespId === 1
      ? 'ByDate'
      : this.graphRespId === 2
        ? 'StartDate'
        : 'ByMonth';
  }

  // Generate a random color for the bars
  getRandomColor() {
    return '#' + Math.floor(Math.random() * 16777215).toString(16);
  }
getXValues(dataset: any[]): any[] {
    if (dataset === this.graphDetails.lstByDateList || dataset === this.graphDetails.lstByWeekDateList) {
      dataset.sort((a, b) => d3.timeParse('%Y-%m-%dT%H:%M:%S')(a[this.getXAxisKey()]).getTime() - d3.timeParse('%Y-%m-%dT%H:%M:%S')(b[this.getXAxisKey()]).getTime());
      return dataset.map((data) => {
        const originalDate = new Date(data[this.getXAxisKey()]);
        return this.datePipe.transform(originalDate, 'MM/dd/yyyy') || '';
      });
    }
    return dataset.map((data) => data[this.getXAxisKey()]);
  }

Answer №1

It's been a couple of years since I last worked with D3, so my memory might be a bit fuzzy. However, I believe the chart you're trying to implement is similar to what I worked on back then.

import { getAllLifecycleHooks } from '@angular/compiler/src/lifecycle_reflector';
import { Component, OnDestroy, Pipe } from '@angular/core';
import { NbColorHelper, NbThemeService } from '@nebular/theme';
import { FactureAdcaisse } from '../../../@core/data/FactureAf';
import { FactureService } from '../../../@core/mock/facture.service';



@Component({
  selector: 'ngx-d3-bar',
  template: `
    <chart echarts [options]="options" class="echart"></chart>

  `,
})
export class D3BarComponent implements OnDestroy {


  listfacture : FactureAf[];
  facture :FactureAf= new FactureAf()

  options: any = {};
  themeSubscription: any;

  totalpric_Y : any[]=[] ;
  totalpric_X : any ;

  constructor(private theme: NbThemeService , private  facttureservice : FactureService) {
  }

  ngAfterViewInit() {
    this.facttureservice.getAllActiveFacture().subscribe((data) => {
      this.listfacture = data;
      console.log("Invoices list",this.listfacture);

      this.totalpric_Y = this.listfacture.map(x=>x.totalprice) ;
      this.totalpric_X = this.listfacture.map(x=>x.name) ;

      console.log("les x",this.totalpric_X)
      console.log("les y",this.totalpric_Y)


      this.themeSubscription = this.theme.getJsTheme().subscribe(config => {




        const colors: any = config.variables;
        const echarts: any = config.variables.echarts;

        this.options = {
          backgroundColor: echarts.bg,
          color: [colors.primaryLight],
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'shadow',
            },
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true,
          },
          xAxis: [
            {
              name: 'Partenaire :',

              type: 'category',
              data:this.totalpric_X,
              axisTick: {
                alignWithLabel: true,
              },
              axisLine: {
                lineStyle: {
                  color: echarts.axisLineColor,
                },
              },
              axisLabel: {
                textStyle: {
                  color: echarts.textColor,
                },
              },
            },
          ],
          yAxis: [
            {
              type: 'value',
              axisLine: {
                lineStyle: {
                  color: echarts.axisLineColor,
                },
              },
              splitLine: {
                lineStyle: {
                  color: echarts.splitLineColor,
                },
              },
              axisLabel: {
                textStyle: {
                  color: echarts.textColor,
                },
              },
            },
          ],
          series: [
            {
              name: 'Total(DT)',
              type: 'bar',
              barWidth: '60%',
              data: this.totalpric_Y,
            },
            console.log('whyyyyyyyyyyyyy',this.totalpric_Y)
          ],
        };
      });


    });
    console.log("les facture active",this.listfacture);
  }

  ngOnDestroy(): void {
    this.themeSubscription.unsubscribe();
  }
}

This example should be easy to understand and implement. Just fetch your data and map it to the appropriate X and Y axes.

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

Tips for alternating the color of <li> or <tr> elements consecutively

Looking to alternate the background color of li or tr elements consecutively. Please note: A static class will not work as I need to call this in a while loop. The desired output should look like: <li>line1</li> //white background <li> ...

Utilizing Angular JS to parse JSON data and showcase it in various tables

Just diving into Angular JS and looking for some guidance. Can someone show me how to parse and showcase JSON Data in separate tables using Angular JS? [ { "id": 0, "isActive": false, "balance": 1025.00, "picture": "htt ...

Creating a sleek navigation bar and sliding feature within the header of your Ionic 2

I am looking to create a sliding header for my website where the gallery hides and the navbar moves to the top as I scroll down, similar to the gif provided. Any help or ideas on how to achieve this would be greatly appreciated. Thank you. https://i.sstat ...

What is the best way to handle exceptions when dealing with MongoDB?

Issue Encountering a problem where the try block fails to capture errors when utilized within the MongoClient's connect function in MongoDB. System Details Operating System: Linux (Mint, Tessa) Node.js Version: v10.16.0 (utilizing ES6 with nodem ...

Having trouble accessing JSON response properties within an Angular.js controller

My Angular.js controller is called 'forecastController'. This is the code for the Angular.js Controller: weatherApp.controller('forecastController', ['$scope','$routeParams','cityService', 'weat ...

What are the steps to switch to a root page after a successful sign-in with Ember-Auth?

After successfully implementing Ember-Auth using a pure token based approach, I am facing a challenge in redirecting users to the root of my app once they sign in. Although I know about actionRedirectable (refer to for details), since I am using a pure t ...

Executing Statements in a Specific Order with Express and Sqlite3

I am having an issue creating a table and inserting an item into it using the node command. Despite my efforts to reorganize my script, the item is being inserted before the table is created. Interestingly, manually inputting the commands in sqlite3 works ...

Animating the background color of a div vertically from the center using CSS3 or jQuery

I am attempting to achieve a similar effect to the right navigation on the following website: Using jQuery to create the effect where the color expands from the center is what I am aiming for. However, since that particular site was built using Flash, I a ...

Encountering a null object reference error in AngularJS when using Internet Explorer

Issue: Encountering an error when AngularJS is being loaded in Visual Studio 2015 via an intranet URL. The error is occurring in the AngularJS library at these specific lines: line 7: if(H(b)||Ta(b)) (for angularjs.min.js) line 322: } ...

Creating an array of objects using Constructors in Typescript

Utilizing TypeScript for coding in Angular2, I am dealing with this object: export class Vehicle{ name: String; door: { position: String; id: Number; }; } To initialize the object, I have followed these steps: constructor() { ...

Error: The specified JavaScript library "angular" is not recognized

Attempting to develop a basic Angular application for searching names from an array in the controller. Each time the app is executed, encountering the error 'Uncaught ReferenceError: angular is not defined' in the console. Despite researching sim ...

sending parameters to package.json scripts

Currently, I am utilizing nodejs and in my package json file, I am required to define a test in the following manner: "test:mine": "cross-env NODE_ENV=test nyc mocha \"./tests/**/objectStore.test.ts\" ", When I execute npm run test, only that s ...

"Troubleshooting: NuxtJs vue-flip feature stuck on front side, not rotating to

I have recently installed the vue-flip plugin in a NuxtJs (VueJS) project that I created using the command: npx create-nuxt-app <project-name>. In the index.vue file, I simply added: <vue-flip active-click="true"> <div slot="front"> ...

Struggling to utilize a personalized filter leads to the error message: "Unable to call a function without a designated call signature."

Trying to use a custom filter from an Angular controller is resulting in the error message: 'Cannot invoke an expression whose type lacks a call signature'. I successfully implemented this on my last project, so I'm confused about what coul ...

Why am I receiving an empty object after making a post request?

Attempting to make a post request to another localhost server as I am using webpack in my project and unsure how to handle the default webpack server. Here is the code for my post request: btn_bag[0].addEventListener('click', (e) => { ...

An issue occurred while trying to use the next() method with passport.authenticate('local') function

My current middleware setup involves the use of passport.js for user authentication before moving on to the next middleware: exports.authenticate = (req, res, next) => { passport.authenticate('local', (err, user, info) => { console.l ...

Utilizing a dynamically created Stripe checkout button

Currently, I am attempting to integrate a checkout button from the Stripe Dashboard into my VueJS Project. I have a feeling that I might not be approaching this in the correct manner, so if you have any advice, I would greatly appreciate it. In order to ...

Retrieve a specific quantity of items from an array

I recently started using Postman to develop some test cases. Many of the responses I receive with the get method contain large arrays with numerous objects (I have simplified them here for readability, but each object actually has over 20 properties). Cu ...

Running multiple Karma and Jasmine projects within a single solution efficiently (and the necessity for Jenkins integration)

In our extensive solution, we are managing various projects with Karma & Jasmine Tests. Utilizing Jenkins for continuous integration, our goal is to streamline the process by running the Karma execute command just once. This means eliminating the need to m ...

Looking to implement pagination in Vue.js - what steps should I take?

Hi there, I'm currently facing an issue while trying to paginate my data. The error message in my console reads: Property or method "$index" is not defined on the instance but referenced during render. Below is my HTML template where I display the da ...