Quantcast
Channel: Ember.JS - Latest topics
Viewing all articles
Browse latest Browse all 4826

Making a D3 chart an Ember Component using InsertElement and didUpdateAttrs

$
0
0

@sorvah wrote:

Hello. I have a D3 chart that re-renders data when a model is refreshed an although I have the chart working I am having a lot of trouble adding it as an Ember component. Here is what I have so far:

(note that the appropriate d3 modules are imported before the component object is extended)

export default Component.extend({
data: [],

  didInsertElement() {
    this.buildChart()
  },

  didUpdateAttrs() {
    this.buildChart()
  },

  buildChart() {
    var data = this.get('data')

    var margin = {top: 35, right: 145, bottom: 35, left: 45},
        width = 650 - margin.left - margin.right,
        height = 450 - margin.top - margin.bottom;

    var svg = select("#chart")
    	.attr("width", width + margin.left + margin.right)
    	.attr("height", height + margin.top + margin.bottom)
    .append("g")
    	.attr("transform","translate(" + margin.left + "," + margin.top + ")");

    var x = scaleBand()
        .rangeRound([0, width])
        .padding(0.1);

    var y = scaleLinear()
        .rangeRound([height, 0]);

    var z = scaleOrdinal()
      .range(["steelblue","darkorange", "red"]);

    svg.append("g")
      .attr("class","x-axis");

    svg.append("g")
      .attr("class", "y-axis");

    var input = selectAll(".opt").property("value");

    selectAll(".opt").on("change", function() {
    	update(data, this.value)
    })

    update(data);

    function update(data) {

    	var keys = ["count1", "count2", "count3"];

    	var series = stack()
    		.keys(keys)
    		.offset(stackOffsetDiverging)
    		(data);

    	x.domain(data.map(d => d.label));

    	y.domain([
    		min(series, stackMin),
    		max(series, stackMax)
    	]).nice();

        var barGroups = svg.selectAll("g.layer")
        	.data(series);

        barGroups.exit().remove();

        barGroups.enter().insert("g", ".x-axis")
          .classed('layer', true);

        svg.selectAll("g.layer")
        	.transition().duration(750)
        	.attr("fill", d => z(d.key));

        var bars = svg.selectAll("g.layer").selectAll("rect")
          .data(function(d) { return d; });

    	bars.exit().remove()
        bars = bars
        	.enter()
        .append("rect")
        	.attr("width", x.bandwidth())
        	.attr("x", d => x(d.data.label))
          .merge(bars)

        bars.transition().duration(750)
        	.attr("y", d => y(d[1]))
        	.attr("height", d => Math.abs(y(d[0])) - y(d[1]));

    	svg.selectAll(".x-axis").transition().duration(750)
    		.attr("transform", "translate(0," + y(0) + ")")
    		.call(axisBottom(x));

    	svg.selectAll(".y-axis").transition().duration(750)
    		.call(axisLeft(y));

    	function stackMin(serie) {
    		return min(serie, function(d) { return d[0]; });
    	}

    	function stackMax(serie) {
    	  return max(serie, function(d) { return d[1]; });
    	}

    }
  }
});

In the first instance the chart renders correctly. The problem occurs when the model refreshes.

Rather than simply transition the bars, the whole chart is re-rendered and is placed ‘on top’ of the existing chart.

I know that this is because when the attrs update I am redrawing the whole chart but I am struggling to divide my code between the insertElement hook and the buildChart function. I tried to move all of the standard elements (such as svg, x, y etc) to insertElement and use setters/getters to reference them in buildChart but I constantly run into ‘is undefined’ errors.

Does anyone have any experience in similar problems?

Posts: 1

Participants: 1

Read full topic


Viewing all articles
Browse latest Browse all 4826

Trending Articles