I've installed the d3-sankey package onto the server with npm. It is now loaded into the node_modules directory, but I am still not able to get the code to see it. Obviously, I am not well versed in this area. I copied the package over to the 'public/assets/app/js/progress' directory and I have tried all sorts of approaches to include the correct path to the d3-sankey package in the 'define' section of the chart.js file, yet I continually get a 404 (Not Found) error being generated from the require.min.js file. Can someone point me to the correct path?
How to get my d3 js script to see the sankey.js file
I'm working on a laravel site where the data is fed from the controller through a jQuery file. I am trying to create a sankey chart, but I can't get the d3 code to recognize the sankey.js file, even though it seems that it is correctly included. The error I am getting is 'Uncaught TypeError: d3.sankey is not a function'.
I have tried embedding the d3 code in the view file html with two "" tags to include the d3 package and the sankey.js file as a plugin. When I pass the data from the js file I have to do it by use of a function and am unable to access the sankey.js file within that function, resulting in the same error as above.
I am now trying it by keeping the d3 code in the jQuery file (chart.js) and passing the completed chart, which to me would be more eloquent, but again I get the "d3.sankey is not a function" error.
This is the view file:
<div class="container">
<div id="visitors-progress" class="block block-condensed {{ getDateRangeKey(getMainControlsSelections()->dateRange) }}">
<div class="app-heading">
<div class="title">
<h2>Visitors Progress Graphed</h2>
<p>Sankey Visualization</p>
</div>
</div>
<div class="block-content">
<div class="row">
<div id="sk-chart">
<div id="sankey_viz"></div>
</div>
</div>
</div>
</div>
</div>
This is the chart.js file:
define([
'../../../../assets/app/js/progress/sankey.js',
'progress/chart/skchart',
'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'
], function(sankey, SkClass, d3) {
var ProgressChartClass = function($element){
// Private vars
var _this = this,
_element = null,
_container = null;
// Public var
this.SkChart = null;
this.data = [];
// Private Methods
var _construct = function () {
if (!_.isUndefined($element)) { _element = $element; }
else { _element = $('#visitors-progress'); }
var element = _element.find("#sk-chart");
if (element.length) {
_this.SkChart = new SkClass(element);
}
$(window).on("resize", _onResize);
};
var _onResize = function () {
_.delay(function(){
_this.Resize();
}, 250);
};
// Public Methods
this.SetLoading = function (option) {
if (_.isUndefined(option)) { option = false; }
if (option) { app.block.loading.start(_element); }
else { app.block.loading.finish(_element); }
};
this.Update = function (data) {
console.log('Sankey, Chart js 41, data for Chart: ', data);
if (_.isUndefined(data) || _.isNull(data)) {
data = {};
}
if (this.SkChart && data) {
//d3 code starts here
var units = "Visitors";
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = 1200 - margin.left - margin.right,
height = 740 - margin.top - margin.bottom;
var formatNumber = d3.format(".0f"), // zero decimal places
format = function (d) {
return formatNumber(d) + " " + units;
},
color = d3.scale.category20();
// append the svg canvas to the page
var svg = d3.select("#sk-chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set the progress diagram properties
var sankey = d3.sankey()
.nodeWidth(36)
.nodePadding(10)
.size([width, height]);
var path = sankey.link();
// load the data
d3.json(data, function (error, graph) {
console.log('Graph: ', graph);
var nodeMap = {};
graph.nodes.forEach(function (x) {
nodeMap[x.name] = x;
});
graph.links = graph.links.map(function (x) {
return {
source: nodeMap[x.source],
target: nodeMap[x.target],
value: x.value
};
});
sankey
.nodes(graph.nodes)
.links(graph.links)
.layout(32);
// add in the links
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function (d) {
return Math.max(1, d.dy);
})
.sort(function (a, b) {
return b.dy - a.dy;
});
// add the link titles
link.append("title")
.text(function (d) {
return d.source.name + " → " +
d.target.name + "\n" + format(d.value);
});
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
.call(d3.behavior.drag()
.origin(function (d) {
return d;
})
.on("dragstart", function () {
this.parentNode.appendChild(this);
})
.on("drag", dragmove));
// add the rectangles for the nodes
node.append("rect")
.attr("height", function (d) {
return d.dy;
})
.attr("width", sankey.nodeWidth())
.style("fill", function (d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
.style("stroke", function (d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
// add in the title for the nodes
node.append("text")
.attr("x", -6)
.attr("y", function (d) {
return d.dy / 2;
})
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function (d) {
return d.name;
})
.filter(function (d) {
return d.x < width / 2;
})
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
// the function for moving the nodes
function dragmove(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
}
});
}
};
this.Resize = function () {
if (_.isNull(this.Graph)) { return; }
this.Graph.configure({
width: _container.width(),
height: _container.height()
});
this.Render();
};
// Init
_construct();
};
return ProgressChartClass;
});
The data does get passed to the d3 code but the execution stops on the line:
var sankey = d3.sankey()
because the code is not seeing the sankey.js file as far as I can tell. Am I not including it correctly at the top of the chart.js file? Or should I include it differently? Any help would be greatly appreciated.
I resolved this by embedding the javascript code that creates the sankey chart in the view file. The code I got for this feature was downloaded from a site that I can't find now, but one of the files in the download was a sankey.js file which I called with a script tag before the script tag that holds the code that creates the chart. I had to revise the jQuery file into 3 files 'js/progress.js', 'js/progress/skchart.js' and 'js/progress/chart/skchart.js'. The Ajax call is in the second jQuery file and the response is updated to the 'displayChart()' function in the progress.blade.php file.
this.Update = function (data) {
if (_.isUndefined(data) || _.isNull(data)) {
data = {};
}
if (this.SkChart && data) {
this.SkChart.Update(displayChart(data));
}
};
Code in the progress.blade.php file:
var displayChart = function(data) {
console.log('Progress blade 100: chart data: ', data);
if (!data || Object.keys(data).length === 0 && data.constructor === Object) {
console.log('Progress blade 402: chart data is undefined');
return '';
}
var units = "Visitors";
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = 1200 - margin.left - margin.right,
height = 740 - margin.top - margin.bottom;
...
Please or to participate in this conversation.