I'm working on a problem where I want to display data in a dashboard both as a chart (via perak:c3) and in a table (via aslagle:reactive-table). My issue is that the data is pulled from a collection in MongoDB, and it's format is instantly amenable to plotting via c3, but needs to be transformed into a local collection to be used by the reactive-table package, as suggested in this answer to a previous question.
When I change the dataset to be displayed I want the chart to be updated, and the table also. This requires changing the values in the local collection, however, which slows things down and so rather than the chart being smoothly redrawn, there is a freeze on the page, and then the new data is displayed.
I have created a sample project on GitHub here so the problem can be replicated easily. If you run the app and select a dataset in your browser you will see exactly what I mean
To see the reactive behaviour I want to preserve in the chart go to client/templates/dashboard/dashboard.html and simply comment out the table template {{> dashboardTable}}
and now change the dataset to see how the chart is smoothly redrawn. Essentially I am trying to ensure both templates dashboardChart and dashboardTable render independently of one another.
UPDATE
Following Michael Floyd's suggestion of using a timeout helped a bit
Meteor.setTimeout(function(){createLocalCollection(data)},200);
but although the chart gets smoothly drawn, when the table finishes being filled, the chart is drawn again. It looks like it jumps to some intermediate state that I can't understand. Here is a video showing what I mean.
I'm adding this as a separate answer because it's a completely different approach.
Use the onRendered callback in d3 to invoke the local collection update.
Where you have:
chart = c3.generate({
bindto: '#dataset-chart',
in dashboard_chart.js, add:
chart = c3.generate({
onrendered: createLocalCollection(),
bindto: '#dataset-chart',
Of course you need to remove createLocalCollection(data) from your event handler.
To avoid having to pass the data context through the onrendered handler in d3 also update your createLocalCollection function to use the reactive variable datasetID that you defined earlier to establish the current dataset:
var createLocalCollection = function() {
var values = My_First_Collection.find({datasetID: datasetID.get()}).fetch();
var tempDoc = {};
local.remove({});
tempDoc = {};
for (var i in values[0].value) {
tempDoc.value = values[0].value[i];
tempDoc.date = values[0].date[i];
local.insert(tempDoc);
}
};
Using this method you let D3 tell you when the chart rendering is done and then your table can start getting populated. The result is an instantaneous chart update followed by the table updating. No mucking with timeouts either.
Remember that js is single threaded. You have two things to and they are going to happen sequentially. What you can do is defer the code that is updating the local collection using Meteor.setTimeout(). This will allow the chart to update first and then your table can update second. I've seen this before where you run a function that updates the DOM (in your case d3 is updating the svg canvas) but the actual screen update gets stuck behind long running js.
I tried this specifically and chart performance was fine.
Meteor.setTimeout(function(){createLocalCollection(data)},500);
Cutting the interval down to 100 allowed the chart to update but then the menu didn't fade out completely until the local collection finished updating.
One thing that I've used with tables and local collections is to only update the local collection document when the corresponding non-local document is being rendered on screen (assuming there's a 1:1 relationship between the original data and the transformed version). This allows the reactive table to load lazily.
Related
I'm using jqBarGraph to draw a graph on a page, but after the page loads, the graph updates with new values. How do I redraw the graph?
I'm updating the array I'm used to initialize the graph and then re-using it to initalize the graph (occurs within an each()).
Initial array:
array_msa_graph_1 = new Array(
[12300, 'MSA', '#88c100'],
[0, 'Gap', '#5e8500'],
[12300, 'ATB', '#74a400']
);
jQuery('#msa_graph_1').jqbargraph({
data: array_msa_graph_1
});
Updating array:
var graph_id = $(this).find('.msa_graph').attr('id');
var array_name = 'array_' + graph_id;
var graph_array = eval(array_name);
var msa_value = getMSA(graph_id);
graph_array[0][0] = msa_value;
$('#' + graph_id).jqbargraph({
data: graph_array
});
However, in the redraw process, it seems the array gets appended to the already existing graph and doesn't update the existing columns and labels.
What am I doing wrong?
Woah, this is quite an old post. I am also implementing bar graph using this plugin. I was having this trouble yesterday. I managed to solve this issue by taking a look at their page source. I found out that the person was able to clear the modification made on the graph. Hence, is this line of code that reset the values. $('#DivforGraph').html(''); //reset graph during dynamic change :)
*I will post the solution here.Maybe it might benefit someone who is facing the same problem as us! Cheers! ^_^
I have inherited a project that is using Highcharts.
The previous dev used the .addSeries method to add all of the series to each chart that was being rendered. From what I've read of Highcharts, it seems like .addSeries is really for adding data dynamically.
The data that is being used to populate the charts are coming from an AJAX request. The old dev's approach was to get the data, render the chart, and then add a series using .addSeries. I was thinking that it might be better to update options.series and then pass the whole thing along to new Highcharts.Chart() for rendering, taking the .addSeries out of the equation.
However, since I'm new with Highcharts, I was hoping to get some feedback on what the better method would be.
You're on a good path, though your question suggests you may simply be looking for preference over a strict right/wrong answer.
From what I've seen, unless you have interactions on the page that would trigger a need to update your chart after it's been drawn, the benefit to using addSerie would be to add some visual flare. Using addSerie, your charts will visually draw themselves in front of the visitor - vs them already being drawn. (I believe HighCharts demo site has some good examples of this.)
I also recently inherited a HighCharts project and am generating a new Highcharts.Chart() using dynamic data by parsing the AJAXed data on the fly. The good news is that all of the charts still have nice visual flare (flare is important) since they don't draw until the AJAXed data is fully loaded. This snippet illustrates how I've been loading dynamic charts, parsing the JSON data on the fly:
$(function () {
var visitsChart;
$(document).ready( function() {
$.getJSON('/json-data-url', function(json){
var visitsChart = new Highcharts.Chart({
chart: {
renderTo: 'visitsContainer',
type: 'spline',
},
title: {
text: 'Test Widget'
},
series: [{
name: 'Speed',
data: [parseInt(json.visits)],
}],
...
});
});
});
});
I won't lie ... I had a few minutes of hair pulling when I got started but now I wish I had more time to work with Highcharts as it's quite fun once you get on a roll. Hope this helps.
I am using the heatmap.js library from pa7 in github successfully to make some heatmap
http://www.patrick-wied.at/static/heatmapjs/example-heatmap-leaflet.html
But when I updated the heatmap with additional data or new data points, the leaflet library does not really update with the additional data points. There seems to be no method that can used for redraw. I dont know if someone has already solved this problem, thats why I am asking to see. Here is the simple portion of my additional script:
ndata=[{lat: 13.59, lon:-17.05, value: 11},{lat: 33.08, lon:-103.24, value: 19}]
testData.data.push.apply(ndata)
layer.addData(testData.data);
There is no errors in the console of Google chrome or no exceptions.
Thanks
There is a redraw method in the layer:
var heatmapLayer = L.TileLayer.heatMap();
// add layer to map
heatmapLayer.redraw()
Having said this, in the latest version, the heatmap layer automatically redraws when you set the data (see https://github.com/pa7/heatmap.js/blob/d09c4e99852b4849e2b4d4f12976a9fce0327ca5/src/heatmap-leaflet.js#L112).
I am using jqPlot javascript library ( http://www.jqplot.com/ ) for graphs and charts in one of my application.
In my application, there are 5-6 pages where this library is used. But I would like to discuss one particular case here.
On 1 of page, I am loading 3 charts. Data for these 3 charts is populated from database tables.
There is different set of queries for each chart. So, populated data for each chart is different too.
Once I have populated data, I have to process it, before providing its input to chart.
What is the problem then:
Problem that I am facing is it takes lots of time for page to render on browser (which is quiet obvious, as first it will form query, then fire that query against database tables, get the data, process on data and give to chart)
One of my friend suggested to implement following thing using ajax. I really liked his solution.
This is what I intend to do:
I would create a page, which will load all the required js/css files for jqPlot library.
There will be 3 sections on that page, where I would put some GIF images indicating that some process is going on (say ajax-loader.gif)
Once page is loaded, it will fire 3 ajax call, one at a time, to fetch each chart.
My Question Is how can I load chart from data received from ajax-call?
jqplot puts data and creates chart in following way (look at example below)
<script class="code" type="text/javascript">
$(document).ready(function(){
var plot2 = $.jqplot ('chart2', [[3,7,9,1,4,6,8,2,5]], {
// Give the plot a title.
title: 'Plot With Options',
// You can specify options for all axes on the plot at once with
// the axesDefaults object. Here, we're using a canvas renderer
// to draw the axis label which allows rotated text.
axesDefaults: {
labelRenderer: $.jqplot.CanvasAxisLabelRenderer
},
// An axes object holds options for all axes.
// Allowable axes are xaxis, x2axis, yaxis, y2axis, y3axis, ...
// Up to 9 y axes are supported.
axes: {
// options for each axis are specified in seperate option objects.
xaxis: {
label: "X Axis",
// Turn off "padding". This will allow data point to lie on the
// edges of the grid. Default padding is 1.2 and will keep all
// points inside the bounds of the grid.
pad: 0
},
yaxis: {
label: "Y Axis"
}
}
});
});
</script>
Since you're using jQuery, you'd use the jQuery Ajax method to fetch the chart data after the page has loaded.
In your success function, your JS code (on the browser) receives the data from your server. Once you have the data, make the call to $.jqplot -- passing in the data you've just received.
To initially show the busy gif, just use the img element as the static content of the chart2 div which will later be the graph's container.
Some tips:
Some browsers don't do well at handling an animated gif while running a js program. So you may want to try a text message ("Loading chart...") in addition to the rotating gif. -- Or update the text messages. Eg start with "Fetching chart data from server" then update to "Processing chart data" once your success function has been called.
Rather than starting all 3 Ajax calls at once, experiment with having the success function for the first chart initiating the second Ajax call. (In addition to it charting the data.)
If you have problems with your Ajax calls, Google for examples and ask a separate question on SO if you still have problems.
I'm using Highchart API to display charts. There are many chart type to display and the idea is let the user choose a chart from a dropdown, make an ajax request and partially update the chart. The good is i can output a custom response, with custom chart options, layout and data.
The problem here is that chart layout and data are inside the script tag in head. An empty div is then populated by the API.
Which pattern should i use to partially update the chart? Sorry for the very noob question, but i'm feeling a bit confused when dealing with something different from updating div with plain text/html as server response.
i was working a little with hightchart, and what i was doing to change the type of chart is calling a function that go's to a php ajax source and create the chart with a table's db result.
each chart's need a diferente table layout, i think.
and that's why i create separeted files for this.
like:
piechart.ajax.php
and a div get the return of ajax call and after that, i call the Highcharts to display the div's result's into a chart.
dont know if this will help you, but may be clear your 'mind'
edit:
html:
<div id="grafic"></div>
js:
$.post("ajax/piechart.ajax.php",
{
cache: false,
},
function(data){
$("#grafic").html(data);
var table = document.getElementById('datatable'),
options = {
chart: {
renderTo: 'grafic',
zoomType: 'xy',
defaultSeriesType: 'pie'
}
};
Highcharts.visualize(table, options);
}
)
Answer myself: make chart a global javascript variable, initialize charts options (not necessary) send an ajax request and return a JSON object that represent the entire chart object, overriding chart global. No need to call redraw. Limitations: as you can't serialize function you can't dynamically override formatters function (e.g. for tooltips).
If you just want to update data call addSeries, setSize, setTitle (and others) methods.
All explained very well here (section 4) and here.