1

I would like to draw a line by d3 with below codes.(http://jsfiddle.net/totaljj/L9n7qnv1)

It draws x,y-axis, but does not enter into the line function when appending d attribute.

You can debug on line number 104 to see that the code does not enter into the line function.

Any help would be appreciated.

<!DOCTYPE html>
<html>
<style>
.axis--x path {
    display: none;
}

.line {
    fill: none;
    stroke: steelblue;
    stroke-width: 1.5px;
}
</style>

<body>
    <!-- Page Content -->

    <div>

            <svg width="430" height="250"></svg>
    </div>

    <section>

        <script src="https://d3js.org/d3.v4.min.js"></script>

        <script>

            var data=
            '{"recordsFiltered":5,"raCounts":[{"name":"comp_name","values":[{"date_":"2016","actual":170.0,"DT_RowId":"row_null"},{"date_":"2015","actual":198.0,"DT_RowId":"row_null"},{"date_":"2015","actual":149.0,"DT_RowId":"row_null"},{"date_":"2014","actual":197.0,"DT_RowId":"row_null"},{"date_":"2014","actual":146.0,"DT_RowId":"row_null"}],"DT_RowId":"row_null"}],"draw":null,"recordsTotal":5}';

            var d = JSON.parse(data);
            draw(d.raCounts);

            function draw(data){

                //svg
                var svg = d3.select("svg"),
                    margin = {top: 100, right: 80, bottom: 30, left: 50},
                    width = svg.attr("width") - margin.left - margin.right,
                    height = svg.attr("height") - margin.top - margin.bottom,
                    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");


                //time
                var parseTime = d3.timeParse("%Y%m%d");

                //domain
//              var x = d3.scaleTime().range([0, width]),
                var x = d3.scaleLinear().range([0, width]),
                    y = d3.scaleLinear().range([height, 0]),
                    z = d3.scaleOrdinal(d3.schemeCategory10);

                //line
                var line = d3.line()
                    .curve(d3.curveBasis)
                    .x(function(d) { 
                        return x(d.date_); })
                    .y(function(d) { 
                        return y(d.actual); });

                  //domain
                  x.domain(d3.extent(data[0].values, function(d) { 
                      return d.date_; }));

                  y.domain([
                    d3.min(data, function(c) { 
                        return d3.min(c.values, function(d) {
                            return d.actual; }); }),
                    d3.max(data, function(c) { 
                        return d3.max(c.values, function(d) {
                            return d.actual; }); })
                  ]);

                  z.domain(data.map(function(c) { 
                      return c.DT_RowId; }));

                  //axis
                  g.append("g")
                      .attr("class", "axis axis--x")
                      .attr("transform", "translate(0," + height + ")")
                      .call(d3.axisBottom(x));

                  g.append("g")
                      .attr("class", "axis axis--y")
                      .call(d3.axisLeft(y))
                    .append("text")
                      .attr("transform", "rotate(-90)")
                      .attr("y", 6)
                      .attr("dy", "0.71em")
                      .attr("fill", "#000")
                      .text("count");

                  var ra = g.selectAll(".ra")
                    .data(data)
                    .enter().append("g")
                      .attr("class", "ra");

                 //ra line
                  ra.append("path")
                      .attr("class", "line")
                      .attr("d",
                              function(d) { return 
                                  line(d.values); })
                      .style("stroke-dasharray", ("1","1"));
            }


            </script>
    </section>

</body>
</html>
tompal18
  • 1,164
  • 2
  • 21
  • 39

3 Answers3

3

JavaScript doesn't always require semicolons at the end of a line. It will automatically insert them in certain situations, and the place where you call your line function is one of them:

                  .attr("d",
                          function(d) { return 
                              line(d.values); })

The fix is therefore to remove the newline after return:

                  .attr("d",
                          function(d) { return line(d.values); })
Community
  • 1
  • 1
Luke Woodward
  • 63,336
  • 16
  • 89
  • 104
3

I think both Gerardo Furtado's answer as well as Luke Woodward's answer have good points, but both circumvent the fact, that OP's solution is somewhat off the beaten track. To make full use of the data binding the typical approach would be something like the following:

//ra line
ra.selectAll("path.line")
  .data(function(d) { return [d.values]; })
  .enter().append("path")
    .attr("class", "line")
    .attr("d", line)

Passing just the line generator function get rids of the automatical semicolon insertion after the return statement. On the other hand, doing the data binding for the path.line element still allows for multiple lines drawn by the same statement.

Have a look at the following snippet for a working example:

  var data =
    '{"recordsFiltered":5,"raCounts":[{"name":"comp_name","values":[{"date_":"2016","actual":170.0,"DT_RowId":"row_null"},{"date_":"2015","actual":198.0,"DT_RowId":"row_null"},{"date_":"2015","actual":149.0,"DT_RowId":"row_null"},{"date_":"2014","actual":197.0,"DT_RowId":"row_null"},{"date_":"2014","actual":146.0,"DT_RowId":"row_null"}],"DT_RowId":"row_null"}],"draw":null,"recordsTotal":5}';

  data = JSON.parse(data).raCounts;


  //svg
  var svg = d3.select("svg"),
    margin = {
      top: 100,
      right: 80,
      bottom: 30,
      left: 50
    },
    width = svg.attr("width") - margin.left - margin.right,
    height = svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");


  //time
  var parseTime = d3.timeParse("%Y%m%d");

  //domain
  //    var x = d3.scaleTime().range([0, width]),
  var x = d3.scaleLinear().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    z = d3.scaleOrdinal(d3.schemeCategory10);

  //line
  var line = d3.line()
    .curve(d3.curveBasis)
    .x(function(d) {
      return x(d.date_);
    })
    .y(function(d) {
      return y(d.actual);
    });

  //domain
  x.domain(d3.extent(data[0].values, function(d) {
    return d.date_;
  }));

  y.domain([
    d3.min(data, function(c) {
      return d3.min(c.values, function(d) {
        return d.actual;
      });
    }),
    d3.max(data, function(c) {
      return d3.max(c.values, function(d) {
        return d.actual;
      });
    })
  ]);

  z.domain(data.map(function(c) {
    return c.DT_RowId;
  }));

  //axis
  g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));

  g.append("g")
    .attr("class", "axis axis--y")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", "0.71em")
    .attr("fill", "#000")
    .text("count");

  var ra = g.selectAll(".ra")
    .data(data)
    .enter().append("g")
    .attr("class", "ra");

  //ra line
  ra.selectAll("path.line")
      .data(function(d) { return [d.values]; })
      .enter().append("path")
    .attr("class", "line")
    .attr("d", line)
    .style("stroke-dasharray", ("1", "1"));
<style> .axis--x path {
  display: none;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}

</style>
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="430" height="250">
</svg>
Community
  • 1
  • 1
altocumulus
  • 21,179
  • 13
  • 61
  • 84
  • 1
    Unfortunately, @GerardoFurtado deleted his answer. I will keep the broken link for a while in case he changes his mind and decides to undelete his post. This won't change my line of argument, though. – altocumulus Jan 02 '17 at 10:56
  • I had deleted it because @Luke solution was better, but since you posted your answer i undeleted it, so the OP can my lazy solution. – Gerardo Furtado Jan 02 '17 at 10:59
  • @GerardoFurtado Great! I think all three answers together wrap it up pretty well and provide a decent lesson on what can go wrong and why it's important to stick to best practices wherever possible. – altocumulus Jan 02 '17 at 11:05
0

You're not passing the correct data to your line generator. It should be:

ra.append("path")
    .attr("class", "line")
    .attr("d", line(data[0].values))
    .style("stroke-dasharray", ("1", "1"));

Here is your updated fiddle: http://jsfiddle.net/67zs8ph7/

PS: this will plot just one line (see comment below)

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • This will work as long as there is exactly one line to plot. If the `data` array contains more than one element, the extra elements will be ignored. – Luke Woodward Jan 02 '17 at 10:20