1

I am trying to make a histogram with d3.js. I am new at javascript and JSON so I need your help.

Basically I am trying to use this code here: How to make a bar chart for time duration with d3?

The shown code here with the data in the code work fine, but how do I refer to my JSON outputted data?

urls.py for my JSON data

url(r'^api/playAround', views.playAround, name='playAround'),

view.py

def playAround(request):
data = Customer.objects.annotate(month=TruncMonth('Update')).values('month').annotate(countItems=Count('id')) 
return JsonResponse(list(data), safe=False)

localhost/api/playAround

[
    {
     "month": "2017-10-01",
     "countItems": 16,
    },
    {
     "month": "2018-04-01",
     "countItems": 1,
    },
    {
     "month": "2018-10-01",
     "countItems": 1,
    },
]

How do I have to change the line

var data =....

that I get my JSON data read and displayed?

The line

d3.json("{% url "playAround" %}", function(error, data) {}

did not work. I think I do not get a main point in d3.js+JSON+javascript

template

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}

.bar:hover {
fill: brown;
}

.axis--x path {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="/static/js/d3.v5.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;

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

var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var data = d3.json("{% url 'playAround' %}", function(error, data) {

x.domain(data.map(function(d) {
return d.month;
}));
y.domain([0, d3.max(data, function(d) {
return d.countItems;
})]);
});

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("text-anchor", "end")
.text("Frequency");

g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
  return x(d.month);
})
.attr("y", function(d) {
  return y(d.countItems);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
  return height - y(d.countItems);
});
kingbrain
  • 116
  • 12
  • You need to grab the data over HTTP(s) using XHR. https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest to do it vanilla, or use a library such as axios https://github.com/axios/axios – jonzlin95 Apr 30 '18 at 19:20
  • Hm...I #don't get the point. As far as I understood Django handles over the data in JSON format and you can fetch it. Here is an example: https://stackoverflow.com/questions/26453916/passing-data-from-django-to-d3?noredirect=1&lq=1 It seems to me that I am dointg sth. wrong with d3.js/javascript using the data. OR did I get you wrong? – kingbrain Apr 30 '18 at 19:23
  • You have built an API endpoint, which is not serving HTML (if I'm understanding your post correctly), since your endpoint only returns a single [] (list). You need to access this endpoint from an HTML page by querying this endpoint and retrieving the data. Then you can feed this data into d3 – jonzlin95 Apr 30 '18 at 19:27
  • So far so good. I have added the views.py. I builded this endpoint which is not a html. So I should be able to access it as in the posted example in the previous post, but I do not know how. :-/ – kingbrain Apr 30 '18 at 19:39
  • What do you mean by "did not work"? What exactly happened? How did you try using it? Does it work if you hardcode the url? `d3.json("localhost/api/playAround")`. If you tried that, did you get a javascript error message in the web browser console? – Håken Lid Apr 30 '18 at 19:54
  • By the way, `d3.json()` should fetch the data directly from an api endpoint. There's no need to use axios, since this functionality is already provided with [d3-fetch](https://github.com/d3/d3-fetch) – Håken Lid Apr 30 '18 at 20:00
  • I have added the template. When I just change the d3.json line due to the original version in https://stackoverflow.com/questions/41708688/how-to-make-a-bar-chart-for-time-duration-with-d3 then I got the error that "map" is not a function. Including x.domain and y.domain in the d3.json statement the error disappears. – kingbrain Apr 30 '18 at 20:28
  • I have tried a lot but still no solution. When I put the data directly in the code everythin works fine, but it seems that there is a problem with parsing or reading the data in JSON format via url. If I put a console.log(data) into the d3.json statement the console doesnot show any data. I don't see the error in the statement?! – kingbrain May 02 '18 at 07:40

1 Answers1

1

I found the solution. You have to do one thing when accessing the JSON objects in contrast to having the data in the code:

As you do not have a global variable of your data, you have to include all the domain or append information within d3.json.

So here is the relevant code:

d3.json("{% url "playAround" %}", function(error, data) {   

    .....   // insert here the x./y.domain and g.append information

});

Plus that the code above is only working with d3.v4.min.js.

kingbrain
  • 116
  • 12