Reputation: 368
I have a variable nest created from d3.json:
d3.json("api_all.php", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
});
var nest = d3.nest()
.key(function(d){
return d.stockName;
})
.entries(data);
However I have another function down the code that needs to use the saved nest data:
var initialgraph = function(stockName) {
/*Filter the data to include only stock of interest*/
var selectStock = nest.filter(function(d) {
return d.key == stockName;
})
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock, function(d) {
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "stockGroups")
.each(function(d) {
y.domain([0, d3.max(data, function(d) { return d.value; })])
});
var initialPath = selectStockGroups.selectAll(".rect")
.data(function(d) {return d.value.year})
.enter()
.append("path")
initialPath
.attr("d", function(d) {
return valueLine(d.values)
})
.attr("class", "rect")
/*Add Y Axis*/
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y))
};
I have yet to find a solution for this problem...I was thinking 1) making the nest variable a global variable or 2) to include the initialgraph function as part of the d3.json data pull.
I have yet to find the right way to do #1 and #2 seems to cause a lot of additional problems for my code. So my bias is just finding a solution to doing #1. What are you suggestions? Thanks.
Upvotes: 0
Views: 1487
Reputation: 28673
Call the initialgraph
with the nest
result as an argument. You only have a valid value in the nest variable when the callback is executing.
var initialgraph = function (stockName, nest) {
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d) { return d.key == stockName; });
//....
};
d3.json("api_all.php", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
});
var nest = d3.nest()
.key(function (d) { return d.stockName; })
.entries(data);
var xExtent = d3.extent(data, function(d) { return d.date; });
x.domain(xExtent);
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
/*Build Dropdown Menu*/
var stockDropDown = d3.select("#dropdown")
stockDropDown
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){
return d.key;
})
.text(function(d){
return d.key;
});
initialgraph("KYOKUYO CO.,LTD.", nest);
});
If initialgraph
is defined inside the callback you have no problem because it is a closure.
Edit
Like answered today about async functions, when d3.json()
returns does not mean the call back is called/finished. Remove the initialgraph()
call from line 134. And call it at the end of the callback.
Edit 2
Needed to look up an example for d3v4 and json to see what the interface is of the d3.json()
. According to the docs it is using fetch and promises but apparently not. The real interface is
d3.json(url, function (error, data) {
// process the data
});
Here is the complete code that shows at least the initial graph based on the json. I have not verified if it draws the correct graph.
Calling y.domain()
in an each()
call does not make sense. The max value is already determined based on the json data
. Maybe it is an idea to use the extend of the d.value
: d3.extend(data, d=>d.value);
The updategraph()
now gets a valid nest
value.
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
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 + ")");
/*Define the rect*/
/*Parse dates*/
var parseDate = d3.timeParse("%Y-%m-%d"),
formatDate = d3.timeFormat("%Y");
/*Set the ranges*/
var x = d3.scaleTime()
.domain([new Date(2002, 0, 1), new Date(2003, 0, 1)])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
/*Create axes*/
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var area = d3.area()
.curve(d3.curveStepAfter)
.y0(y(0))
.y1(function(d) { return y(d.value); });
var areaPath = g.append("path")
.attr("clip-path", "url(#clip)")
.attr("fill", "steelblue");
var yGroup = g.append("g");
var xGroup = g.append("g")
.attr("transform", "translate(0," + height + ")");
var zoom = d3.zoom()
.scaleExtent([1 / 4, 8])
.translateExtent([[-width, -Infinity], [2 * width, Infinity]])
.on("zoom", zoomed);
var zoomRect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("pointer-events", "all")
.call(zoom);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
/*Import Data from API*/
d3.json("api_all.php", function(error, data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
});
var nest = d3.nest()
.key(function(d){
return d.stockName;
})
.entries(data);
/*Scale range of the data*/
var xExtent = d3.extent(data, function(d) { return d.date; });
x.domain(xExtent);
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
/*Build Dropdown Menu*/
var stockDropDown = d3.select("#dropdown");
stockDropDown
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){
return d.key;
})
.text(function(d){
return d.key;
});
stockDropDown.on("change", function() {
var selectedStock = d3.select(this)
.select("select")
.property("value");
updateGraph(selectedStock, nest);
});
initialgraph("KYOKUYO CO.,LTD.", nest);
});
/*Function to create initial graph*/
var initialgraph = function(stockName, nest) {
/*Filter the data to include only stock of interest*/
var selectStock = nest.filter(function(d) {
return d.key == stockName;
})
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock, function(d) {
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "stockGroups")
// .each(function(d) {
// y.domain([0, d3.max(data, function(d) { return d.value; })]);
// })
;
var initialPath = selectStockGroups.selectAll(".rect")
.data(function(d) {return d.value.year})
.enter()
.append("path")
initialPath
.attr("d", function(d) {
return valueLine(d.values)
})
.attr("class", "rect")
/*Add Y Axis*/
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y))
};
/*Create initial graph*/
//initialgraph("KYOKUYO CO.,LTD.");
/*Update the data*/
var updateGraph = function(stockName, nest) {
var selectStock = nest.filter(function(d) {
return d.key == stockName;
})
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock)
.each(function(d) {
y.domain([0, d3.max(data, function(d) { return d.value; })])
});
selectStockGroups.selectAll("path.rect")
.data(function(d) {return d.value.year;}, function(d) {return d.key})
.transition()
.duration(1000)
.attr("d", function(d) {
return valueLine(d.values)
})
}
/*Zoom function*/
function zoomed() {
var xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
Upvotes: 1