I am writing a d3.js-based real time chart directive. The structure looks like this:
myDirective.js:
app.directive('myDirective', function(socketio) {
return {
restrict: 'EA',
templateUrl: '../../views/partials/chart.html',
controller: DataController,
link: function(scope, elem, attrs, ctrl) {
scope.Watch = scope.$watch(function() {
return ctrl.data;
}, function(newVal) {
// d3 code part
});
socketio.on(event, function(newdata) {
ctrl.data.push(newdata);
// redraw chart
});
}
};
});
In the above d3 code part, I refer to http://bl.ocks.org/gniemetz/4618602. The main code is almost the same and the chart is displayed well.
Now I want to use socket.io to update the chart via code
socketio.on(event, function(newdata) {
ctrl.data.push(newdata);
// redraw chart
});
but do not know how to redraw the chart efficiently with the updated 'ctrl.data'. I know in Morris.js, we could do this by '.setData(ctrl.data)' method, but do not know how to update in d3. Any idea?
ps: I tried to copy/paste the d3 code above to this place, but there was always an error said: "TypeError: t.slice is not a function"
Thanks,
The quick and dirty way: every time you have an update, empty the <div>
and redraw the entire graph.
The more elegant way: write your own update
function to add new data. This is by far the most visually appealing way, since the graph will actually be animated. D3 is well built for this sort of thing, so its by no means a stretch of its capabilities. This will take a bit longer, but usually provides a much more pleasing experience, as seen on some of the graphs in D3's gallery (examples: http://square.github.io/crossfilter/ , http://bl.ocks.org/NPashaP/96447623ef4d342ee09b) Here's how I would do it using your desired graph:
- Keep your code the same
- Add an update function, say named
Update()
, that's called in the socketio.on(...)
section where you commented // redraw chart
Update()
will then redefine all D3 variables and animate the change
Update()
will essentially do the above bullet by performing a subset of the steps used in creating the graph from scratch. Here's an overview of what it'll do: rescale both the x and y axes, graphically update the axes, translate the original points and lines to their new positions on new axes, add new points, and add any lines it may need to complete the graph
I'm working on a jsFiddle for you with a working Update
to demo the above and, hopefully, implementing it in your code will be simple. I'll edit this post when its done, but I wanted to give you the "quick" answer while I work on it in an attempt to help you meanwhile. If you want some reading meanwhile, check out
http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/
UPDATE
https://jsfiddle.net/npzjLng9/2/. I took the example you provided and had to modify the data to be loaded to be local, not from a text file; however, it is in the same format, and also a lot less entries for readability. Other than that, I did not make any changes to the example. Here's what I added: scroll down and find the last function, Update
. Here is where the update and animation happens. Notice the flow of the function:
- "update" the data as if your
socket.io
was the one who received it and then appended it to the dataset.
- Redefine the axes
- Redefine the points and paths
- Specify a transition duration (feel free to play with whatever number suits you)
- Actually update and animate changes on the plot
I added a button to simulate a socket.io
receiving event.
For your application, ignore the data.push
and console.log
in the Update()
function and I think that's all you'd need -- other than pointing the data to your ctrl.data
array and running the Update()
function in your socketio.on(...)
, of course.
The same basic outline applies to animating/updating most graphs.
I hope this helps you!