Nikhil More
Nikhil More

Reputation: 39

How to update any d3js chart for displaying real-time data?

I am using a template forbasic d3js bar chart, which is working fine with static data. but I need to update it with dynamic/real time data. I need to use data from this array:

data: { date: Date, signal: string, value: number }[] = [];

showChart(): void {
    if (!this.isChartShown) {
      this.isChartShown = true;

      const element = this.chartContainer.nativeElement;

      const svgElement = d3.select(this.chartContainer.nativeElement).append('svg')
      .attr('width', element.offsetWidth)
      .attr('height', element.offsetHeight);

      const contentWidth = element.offsetWidth - this.margin.left - this.margin.right;
      const contentHeight = element.offsetHeight - this.margin.top - this.margin.bottom;

      const x = d3
        .scaleBand()
        .rangeRound([0, contentWidth])
        .padding(0.1)
        .domain(this.data.map(d => d.date.toString()));

      const y = d3
        .scaleLinear()
        .rangeRound([contentHeight, 0])
        .domain([0, d3.max(this.data, d => d.value)]);

      const g = svgElement.append('g')
        .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

      g.append('g')
        .attr('class', 'axis axis--x')
        .attr('transform', 'translate(0,' + contentHeight + ')')
        .call(d3.axisBottom(x));

      g.append('g')
        .attr('class', 'axis axis--y')
        .call(d3.axisLeft(y).ticks(10, '%'))
        .append('text')
          .attr('transform', 'rotate(-90)')
          .attr('y', 6)
          .attr('dy', '0.71em')
          .attr('text-anchor', 'end')
          .text('Frequency');

      g.selectAll('.bar')
        .data(this.data)
        .enter().append('rect')
          .attr('class', 'bar')
          .attr('x', d => x(d.date.toString()))
          .attr('y', d => y(d.value))
          .attr('width', x.bandwidth())
          .attr('height', d => contentHeight - y(d.value));
    }
  }

Upvotes: 1

Views: 1054

Answers (1)

mfilippo
mfilippo

Reputation: 134

As described in the General Update Pattern (an example here) you should think in terms of joining data and DOM elements. You can find many resources and good examples about this topic.

In your case you could do the following:

// This selection represents the update selection (DOM elements with matched data)
var barSelection = g.selectAll('.bar').data(this.data);

// The enter selection contains DOM elements that will match new unmatched data
barSelection.enter()
    .append('rect')
    .attr('class', 'bar')
    .attr('x', d => x(d.date.toString()))
    .attr('y', d => y(d.value))
    .attr('width', x.bandwidth())
    .attr('height', d => contentHeight - y(d.value));

// The exit selection contains DOM elements that are no longer matched with data
barSelection.exit()
    remove();

Of course you need to invoke this pattern every time your this.data changes. Since most of your code should be executed only once (chart initialization) it would be better to move this portion in another function so as to be called on demand. This will require that your g object is reachable from inside that function (you could pass it as an object or assign an id to it and use d3 to select it).

Upvotes: 2

Related Questions