Skip to content
This repository has been archived by the owner on Sep 7, 2020. It is now read-only.

SVG not appearing? #47

Closed
sandysaders opened this issue Jun 14, 2016 · 9 comments
Closed

SVG not appearing? #47

sandysaders opened this issue Jun 14, 2016 · 9 comments

Comments

@sandysaders
Copy link

sandysaders commented Jun 14, 2016

I'm currently writing a a little lib for my job using D3 and React. So I decided to utilize your library to try to get it done efficiently between React and D3 Also, thanks for this library it is great!

Im trying to write a reusable BarGraph component. While I was working on this step by step I saw the bars working throughout. However as soon as I started adding axis to the component it no longer rendered the bars that model data. It's odd because I inspect the console and the rect are rendered to the DOM however there is nothing displayed aside from the axis data. I've been trying to get this working for 2 days now and am at a loss.

BarGraphComponent

class BarGraph extends React.Component {
  render () {
    const {data, width, height} = this.props;
    const innerW = width - 70,
          innerH = height - 50;

    var x = d3.scale.ordinal()
        .domain(data.map((d) => d.name))
        .rangeRoundBands([0, innerW], .1);

    var y = d3.scale.linear()
        .domain([0, d3.max(data, (d) => d.value)])
        .range([innerH, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient('left');

    var chart = d3.select(ReactFauxDOM.createElement('svg'))
      .attr('width', width)
      .attr('height', height)
    .append('g')
      .attr('transform', `translate(${40}, ${20})`);

    chart.append('g')
      .attr('class', 'axis x')
      .attr('transform', `translate(0, ${innerH})`)
      .call(xAxis)

    chart.append('g')
      .attr('class', 'axis y')
      .call(yAxis);

    chart.selectAll('.bar')
      .data(data)
    .enter().append('rect')
      .attr('class', 'bar')
      .attr('x', (d) => x(d.name))
      .attr('y', (d) => y(d.value))
      .attr('height', (d) => innerH - y(d.value))
      .attr('width', x.rangeBand());

    return chart.node().toReact();
  }
}

BarGraph.proptypes = {
  width: React.PropTypes.number,
  height: React.PropTypes.number,
  data: React.PropTypes.array,
}

The code above only renders this:
screen shot 2016-06-14 at 6 34 32 pm

Elements rendered in the console
screen shot 2016-06-14 at 6 35 42 pm

It is based off of this tutorial from the creator of D3:
https://bost.ocks.org/mike/bar/3/

If the problem is obvious or you can tell what I'm doing wrong please let me know I've been having a hard time debugging this mostly because I'm a bit newer to d3.

@Olical
Copy link
Owner

Olical commented Jun 15, 2016

Two days! Ouch! I hope it's resolved soon. Just a hunch, but could it be this?

    var chart = d3.select(ReactFauxDOM.createElement('svg'))
      .attr('width', width)
      .attr('height', height)
    .append('g')
      .attr('transform', `translate(${40}, ${20})`);

That append looks suspicious, now chart is equal to that g tag, not the svg. Try this instead.

    var chart = d3.select(ReactFauxDOM.createElement('svg'))
      .attr('width', width)
      .attr('height', height)

    chart
      .append('g')
      .attr('transform', `translate(${40}, ${20})`);

@sandysaders
Copy link
Author

That seemed to have fixed the problem, Thank you so much @Olical! I'm not too experienced but I'm not sure what the difference is exactly/ why it behaves differently when seperated. How can I know/prevent in the future that I know whether the chart or root node being operated on does not change when chaining?

@Olical
Copy link
Owner

Olical commented Jun 15, 2016

Excellent! I'll try to explain.

// This returns a faux SVG element, as you're aware.
ReactFauxDOM.createElement('svg')

// This returns a faux SVG element wrapped with D3, anything you do to it (.attr etc) will alter that SVG element.
d3.select(ReactFauxDOM.createElement('svg'))

// When you use things like .attr they return the original element, so this statement still returns the wrapped SVG.
d3.select(ReactFauxDOM.createElement('svg'))
  .attr('width', width)

// If you use .append however, it'll change the chain to point to that new element, not the SVG!
d3.select(ReactFauxDOM.createElement('svg'))
  .attr('width', width) // operates on svg
  .append('g')
  .attr('width', width) // operates on the g!

// So, if you want to hold onto your reference to the SVG, you must place the reference in a variable BEFORE you create any children.
var svg = d3.select(ReactFauxDOM.createElement('svg'))
  .attr('width', width) // operates on svg

var g = svg.append('g')

This isn't a ReactFauxDOM thing, it's a D3 thing, but I hope this helped!

@sandysaders
Copy link
Author

@Olical, thank you for taking the time out to explain to me.

@Olical
Copy link
Owner

Olical commented Jun 15, 2016

No problem, happy for me to close this now?

@sandysaders
Copy link
Author

@Olical Yes, thank you again!

@navarr0
Copy link

navarr0 commented Jul 13, 2016

Hi, I'm having a very similar problem to @sandysaders. I'm trying to create a Treemap, and I've managed to do it inelegantly by directly manipulating the DOM with D3. I have tried to make it more React friendly by using your library, except that the same code when transformed to use your library doesn't display the treemap in question.

My transformed code:

var Chart = React.createClass({

  render: function (){
    var d3 = require('d3');
    require("d3-selection-multi");
    require("d3-scale");

      var width = 960,
          height = 1060;

      var format = d3.format(",d");

      var color = d3.scaleOrdinal()
          .range(d3.schemeCategory10
              .map(function(c) { c = d3.rgb(c); c.opacity = 0.6; return c; }));

      var stratify = d3.stratify()
          .parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); });

      var treemap = d3.treemap()
          .size([width, height])
          .padding(1)
          .round(true);

      var faux = ReactFauxDom.createElement("div");
      d3.csv("https://gist.githubusercontent.com/mbostock/6bbb0a7ff7686b124d80/raw/ad76dfe04750a81e108536aeee3fba8b3453fed4/flare.csv", type, function(error, data) {

        if (error) throw error;

        var root = stratify(data)
            .sum(function(d) { return d.value; })
            .sort(function(a, b) { return b.height - a.height || b.value - a.value; });

        treemap(root);

          var tNode = d3.select(faux).selectAll(".node")
          .data(root.leaves())
          .enter()

          tNode.append("div")
            .attr("class", "node")
            .attr("title", function(d) { return d.id + "\n" + format(d.value); })
            .style("left", function(d) { return d.x0 + "px"; })
            .style("top", function(d) { return d.y0 + "px"; })
            .style("width", function(d) { return d.x1 - d.x0 + "px"; })
            .style("height", function(d) { return d.y1 - d.y0 + "px"; })
            .style("background", function(d) { while (d.depth > 1) d = d.parent; return color(d.id); })

          tNode.append("div")
            .attr("class", "node-label")
            .text(function(d) { return d.id.substring(d.id.lastIndexOf(".") + 1).split(/(?=[A-Z][^A-Z])/g).join("\n"); })

          tNode.append("div")
            .attr("class", "node-value")
            .text(function(d) { return format(d.value); });
      });
  return faux.toReact()
}
});

The div that it's supposed to be in returns, but there's nothing inside of it. All that's shown is an empty div.
screen shot 2016-07-13 at 4 32 51 pm

I'm assuming this is a problem with D3 as well, because the actual element seems to return to the console without issue. I tried implementing the fix you had described earlier above, but it was to no avail. The problem could very well be obvious, since I am also new to D3 and your library. Thank you so much for your help and for the library, @Olical!

I'm using Mike Bostock's tutorial for Treemap:
http://bl.ocks.org/mbostock/6bbb0a7ff7686b124d80

The code I had which functions:

var Chart = React.createClass({
render: function (){

    var d3 = require('d3');
    require("d3-selection-multi");
    require("d3-scale");

      var width = 960,
          height = 1060;

      var format = d3.format(",d");

      var color = d3.scaleOrdinal()
          .range(d3.schemeCategory10
              .map(function(c) { c = d3.rgb(c); c.opacity = 0.6; return c; }));

      var stratify = d3.stratify()
          .parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); });

      var treemap = d3.treemap()
          .size([width, height])
          .padding(1)
          .round(true);
    d3.csv("https://gist.githubusercontent.com/mbostock/6bbb0a7ff7686b124d80/raw/ad76dfe04750a81e108536aeee3fba8b3453fed4/flare.csv", type, function(error, data) {

        if (error) throw error;

        var root = stratify(data)
            .sum(function(d) { return d.value; })
            .sort(function(a, b) { return b.height - a.height || b.value - a.value; });

        treemap(root);

        var svg = d3.select("#graph").append("svg");
          d3.select("#graph").selectAll(".node")
          .data(root.leaves())
          .enter().append("div")
            .attr("class", "node")
            .attr("title", function(d) { return d.id + "\n" + format(d.value); })
            .style("left", function(d) { return d.x0 + "px"; })
            .style("top", function(d) { return d.y0 + "px"; })
            .style("width", function(d) { return d.x1 - d.x0 + "px"; })
            .style("height", function(d) { return d.y1 - d.y0 + "px"; })
            .style("background", function(d) { while (d.depth > 1) d = d.parent; return color(d.id); })
          .append("div")
            .attr("class", "node-label")
            .text(function(d) { return d.id.substring(d.id.lastIndexOf(".") + 1).split(/(?=[A-Z][^A-Z])/g).join("\n"); })
          .append("div")
            .attr("class", "node-value")
            .text(function(d) { return format(d.value); });
      });
}
});

@Olical
Copy link
Owner

Olical commented Jul 14, 2016

I'm on holiday at the moment, but at a quick glance I'd say it's because
the csv fetching method you're calling is asynchronous, whereas the render
function must be synchronous. You should fetch the csv ahead of time so
you're not relying on async callbacks.

On 14 Jul 2016 01:41, "navarr0" notifications@github.com wrote:

Hi, I'm having a very similar problem to @sandysaders
https://github.com/sandysaders. I'm trying to create a Treemap, and
I've managed to do it inelegantly by directly manipulating the DOM with D3.
I have tried to make it more React friendly by using your library, except
that the same code when transformed to use your library doesn't display the
treemap in question.

My transformed code:

var Chart = React.createClass({

render: function (){
var d3 = require('d3');
require("d3-selection-multi");
require("d3-scale");

  var width = 960,
      height = 1060;

  var format = d3.format(",d");

  var color = d3.scaleOrdinal()
      .range(d3.schemeCategory10
          .map(function(c) { c = d3.rgb(c); c.opacity = 0.6; return c; }));

  var stratify = d3.stratify()
      .parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); });

  var treemap = d3.treemap()
      .size([width, height])
      .padding(1)
      .round(true);

  var faux = ReactFauxDom.createElement("div");
  d3.csv("https://gist.githubusercontent.com/mbostock/6bbb0a7ff7686b124d80/raw/ad76dfe04750a81e108536aeee3fba8b3453fed4/flare.csv", type, function(error, data) {

    if (error) throw error;

    var root = stratify(data)
        .sum(function(d) { return d.value; })
        .sort(function(a, b) { return b.height - a.height || b.value - a.value; });

    treemap(root);

      var tNode = d3.select(faux).selectAll(".node")
      .data(root.leaves())
      .enter()

      tNode.append("div")
        .attr("class", "node")
        .attr("title", function(d) { return d.id + "\n" + format(d.value); })
        .style("left", function(d) { return d.x0 + "px"; })
        .style("top", function(d) { return d.y0 + "px"; })
        .style("width", function(d) { return d.x1 - d.x0 + "px"; })
        .style("height", function(d) { return d.y1 - d.y0 + "px"; })
        .style("background", function(d) { while (d.depth > 1) d = d.parent; return color(d.id); })

      tNode.append("div")
        .attr("class", "node-label")
        .text(function(d) { return d.id.substring(d.id.lastIndexOf(".") + 1).split(/(?=[A-Z][^A-Z])/g).join("\n"); })

      tNode.append("div")
        .attr("class", "node-value")
        .text(function(d) { return format(d.value); });
  });

return faux.toReact()
}
});

The div that it's supposed to be in returns, but there's nothing inside of
it. All that's shown is an empty div.
[image: screen shot 2016-07-13 at 4 32 51 pm]
https://cloud.githubusercontent.com/assets/20448224/16823248/8ae9e30a-4917-11e6-9113-20e1e79976a5.png

I'm assuming this is a problem with D3 as well, because the actual element
seems to return to the console without issue. I tried implementing the fix
you had described earlier above, but it was to no avail. The problem could
very well be obvious, since I am also new to D3 and your library. Thank you
so much for your help and for the library!

I'm using Mike Bostock's tutorial for Treemap:
http://bl.ocks.org/mbostock/6bbb0a7ff7686b124d80

The code I had which functions:

var Chart = React.createClass({render: function (){

var d3 = require('d3');
require("d3-selection-multi");
require("d3-scale");

  var width = 960,
      height = 1060;

  var format = d3.format(",d");

  var color = d3.scaleOrdinal()
      .range(d3.schemeCategory10
          .map(function(c) { c = d3.rgb(c); c.opacity = 0.6; return c; }));

  var stratify = d3.stratify()
      .parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); });

  var treemap = d3.treemap()
      .size([width, height])
      .padding(1)
      .round(true);
d3.csv("https://gist.githubusercontent.com/mbostock/6bbb0a7ff7686b124d80/raw/ad76dfe04750a81e108536aeee3fba8b3453fed4/flare.csv", type, function(error, data) {

    if (error) throw error;

    var root = stratify(data)
        .sum(function(d) { return d.value; })
        .sort(function(a, b) { return b.height - a.height || b.value - a.value; });

    treemap(root);

    var svg = d3.select("#graph").append("svg");
      d3.select("#graph").selectAll(".node")
      .data(root.leaves())
      .enter().append("div")
        .attr("class", "node")
        .attr("title", function(d) { return d.id + "\n" + format(d.value); })
        .style("left", function(d) { return d.x0 + "px"; })
        .style("top", function(d) { return d.y0 + "px"; })
        .style("width", function(d) { return d.x1 - d.x0 + "px"; })
        .style("height", function(d) { return d.y1 - d.y0 + "px"; })
        .style("background", function(d) { while (d.depth > 1) d = d.parent; return color(d.id); })
      .append("div")
        .attr("class", "node-label")
        .text(function(d) { return d.id.substring(d.id.lastIndexOf(".") + 1).split(/(?=[A-Z][^A-Z])/g).join("\n"); })
      .append("div")
        .attr("class", "node-value")
        .text(function(d) { return format(d.value); });
  });

}
});


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#47 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AATPXRtYYhFZWJQMYn05ucdCJdM84I0Hks5qVXewgaJpZM4I1xxB
.

@navarr0
Copy link

navarr0 commented Jul 18, 2016

Thank you for the advice. How would I go about doing that? Should I just make a function that stores the data into a global (via a call to d3.csv) before I call that class to render? I've also read that it would be possible to pre-load it all via d3.text, but I don't know whether that would work or not. Every time I have tried to pre-load it in via a global in the constructor, the data comes out as "undefined".

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants