reactive chart with chart.js in meteor - javascript

i am using chart.js to generate charts in a meteor app.
Here is my code
function drawChart(){
var data = [
{
value: Policies.find({'purchased_cover.trip_type': 'Single Trip'}).count(),
color:"#F38630"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Annual Multi-Trip'}).count(),
color : "#E0E4CC"
},
{
value : Policies.find({'purchased_cover.trip_type': 'Backpacker'}).count(),
color : "#69D2E7"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Golf Annual'}).count(),
color : "green"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Golf'}).count(),
color : "red"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Winter Sports Annual'}).count(),
color : "yellow"
}
]
var ctx = $("#pieChart").get(0).getContext("2d");
var myPieChart = new Chart(ctx);
new Chart(ctx).Pie(data);
}
Template.charts.rendered = function(){
drawChart();
};
i have few helpers to display the count in html templates and it works fine whenever the counts changes but the chart is not changing until i reload the page..i want the chart to be reactive to the changes in the collection.

You can use Tracker.autorun to rerun drawChart whenever reactive data sources it depends on change:
if (Meteor.isClient) {
function drawChart() {
...
}
Tracker.autorun(drawChart());
}

Related

How to create chart in google sheets with javascript?

I am using javascript to create Google-sheets-document with user data. The document is saved on the user's Drive.
I can't figure out how to make a graph from the data i have inserted. I am using vanilla javascript with the Google sheets API.
It would probably look something like this:
function createGraph() {
gapi.client.sheets.graph
.create({
properties: {
type(?): 'Pie'
spreadsheetid: //some id
range: 'A1:A10'
},
})
}
EDIT: To specify, i want to insert the graph to the sheets-document that i have created, not to the website.
If you want to add the chart to your spreadsheet, you can use Sheets API's AddChartRequest, as part of the spreadsheets.batchUpdate method.
Code snippet:
On broad terms, your request would look like this (check the reference below in order to build the request body in detail):
const payload = {
"requests": [
{
"addChart": {
"chart": {
"spec": { // Chart type, range source, etc.
"pieChart": { // Pie chart specification
// object (PieChartSpec)
}
// rest of ChartSpec properties
},
"position": { // Where the chart will be located
// object (EmbeddedObjectPosition)
}
}
}
}
]
}
const params = {
spreadsheetId = "YOUR-SPREADSHEET-ID",
body = payload
}
gapi.client.sheets.spreadsheets.batchUpdate(params);
Render chart in browsers and mobile devices:
In case you just wanted to render the chart in a browser, but not add it to your spreadsheet, you would use Google Charts (see Visualization: Pie Chart, for example).
Reference:
Sheets API > Charts
EmbeddedChart
ChartSpec
EmbeddedObjectPosition
PieChartSpec
Refer this example
<html>
<head>
<!--Load the AJAX API-->
<script
type="text/javascript"
src="https://www.gstatic.com/charts/loader.js"
></script>
<script type="text/javascript">
var data;
var chart;
// Load the Visualization API and the piechart package.
google.charts.load("current", { packages: ["corechart"] });
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
// Create our data table.
data = new google.visualization.DataTable();
data.addColumn("string", "Topping");
data.addColumn("number", "Slices");
data.addRows([
["Mushrooms", 3],
["Onions", 1],
["Olives", 1],
["Zucchini", 1],
["Pepperoni", 2]
]);
// Set chart options
var options = {
title: "How Much Pizza I Ate Last Night",
width: 400,
height: 300
};
// Instantiate and draw our chart, passing in some options.
chart = new google.visualization.PieChart(
document.getElementById("chart_div")
);
chart.draw(data, options);
}
</script>
</head>
<body>
<!--Div that will hold the pie chart-->
<div id="chart_div" style="width: 400; height: 300;"></div>
</body>
</html>
Referred from
https://developers.google.com/chart/interactive/docs/drawing_charts
I solved it. Thanks for the help! This worked for me.
function createGraphv2(spreadsheetIdGraph, endIndex) {
var params = {
// The spreadsheet to apply the updates to.
spreadsheetId: spreadsheetIdGraph, // TODO: Update placeholder value.
};
var batchUpdateSpreadsheetRequestBody = {
// A list of updates to apply to the spreadsheet.
// Requests will be applied in the order they are specified.
// If any request is not valid, no requests will be applied.
requests: [
{
addChart: {
chart: {
spec: {
title: 'Rapport',
basicChart: {
chartType: 'COLUMN',
legendPosition: 'BOTTOM_LEGEND',
axis: [
//X-AXIS
{
position: "BOTTOM_AXIS",
title: "FORBRUK"
},
//Y-AXIS
{
position: "LEFT_AXIS",
title: "TID"
}
],
series: [
{
series: {
sourceRange: {
sources: [
{
sheetId: 0,
startRowIndex: 0,
endRowIndex: endIndex,
startColumnIndex: 5,
endColumnIndex: 6,
},
],
},
},
targetAxis: "LEFT_AXIS"
}
]
}
},
position : {
newSheet : 'True'
}
},
}
}
],
// TODO: Add desired properties to the request body.
};
var request = gapi.client.sheets.spreadsheets.batchUpdate(
params,
batchUpdateSpreadsheetRequestBody
);
request.then(
function (response) {
// TODO: Change code below to process the `response` object:
console.log(response.result);
},
function (reason) {
console.error("error: " + reason.result.error.message);
}
);
}

Chart js old chart data not clearing

This question has been asked many times and I went through most of them but non of them helped me finding a solution.
I am generating couple of bar charts using a for loop as a part of reporting functionality.
I am using node.js with Express Handlebars.
My page looks like:
<div class="row report-charts">
<div class="col-md-12">
{{#buildings}}
<div class="col-md-6">
<h4>{{Name}}</h4>
<canvas id="{{idBuildings}}" width="200" height="80"></canvas>
</div>
{{/buildings}}
</div>
</div>
My js code looks like:
$('.case-report-btn').click(function(){
$.ajax({
type: 'post',
url: '/reports/cases/filter',
data : {
StartDate : $('.start-ms-time-hidden').val(),
EndDate : $('.end-ms-time-hidden').val(),
ReportKey : $('.cases-filter-type').val()
},
dataType: 'json',
success: function(res) {
$('.report-charts').show();
for(key in res) {
var innerObj = res[key]; //gives the inner obj
var ctx = document.getElementById(key); //the idBuildings
var labels = [];
var data = [];
var buildingName = innerObj.Name;
for(innerKey in innerObj) {
if(innerKey != 'Name' && innerKey != 'Total') {
labels.push(innerKey);
data.push(innerObj[innerKey]);
}
}
var options = {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: buildingName,
data: data,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fixedStepSize: 1
}
}]
}
}
}
var myChart = new Chart(ctx, options);
}
$('#pleaseWaitDialog').modal('hide');
},
error: function(err) {
$('#pleaseWaitDialog').modal('hide');
bootbox.alert('Error: ' + err);
}
});
});
So basically, I am using for loop to generate multiple charts on the page. Inside the loop I declared the chart variable, every time I change the report parameters and hit the button, the new chart is generated. But when I hover over it, the old one still shows up.
Now I am not sure where I should be putting the myChart.destroy() or myChart.clear() methods. I also tried moving the myChart declaration outside the for loop but it didn't help either.
Any suggestions on how to handle this?
I think there are a few ways to do it. You can update your chart data if the chart already exist. Here two functions you can use:
function removeData(chart) {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
function addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
First you have to remove all your data and then add the new data.
If you want to destroy the chart and create it again you have to save your variable as global. To do this you have yo declare your variable like window.myChart and then before create the new chart, something like this:
if (window.myChart) window.myChart.destroy();
window.myChart = new Chart(ctx, options);
Another way you can try is removing your canvas and creating another one. Something like this:
$('#your_canvas').remove();
$('#your_canvas_father').append('<canvas id="your_canvas"></canvas>');

how do i set json data set as labels for graph plot with c3.js

I have a line graph generated with c3.js with json data
the current chart is very simple
var chart = c3.generate({
bindto: '.balanceChart',
data: {
url: '/data',
mimeType:'json'
}
});
json data:
{
data1: [1000,1240,1270,1250,1280]
data2: [1000,240,30,-20,30]
}
chart looks good and is there
but it is currently plotting both sets of data
what i would like is for data2 to be the tooltip value of the plot
You can hide data2 from displaying like so
data: {
...
hide: ['data2']
}
From http://c3js.org/reference.html#data-hide
And use tooltip.format.value to change the tooltip display
tooltip: {
format: {
value: function (value, ratio, id, index) {
// return chart.data.values("data2")[index]; // if still wanting to use data2
// or get rid of data2 completely using this
var vals = chart.data.values(id); // id will be 'data1', vals will then be data1 array
return vals[index] - (index === 0 ? 0 : vals[index - 1]);
}
}
}
http://c3js.org/reference.html#tooltip-format-value
tooltip.format.title and tooltip.format.name will also be useful here to communicate to a user the value isn't actually that of data1 (maybe just changing the title to "Delta Data1")

AngularJS ng-repeat: Dynamically render/bind canvas inside loop

Relatively new to the world of AngularJS, enjoying it so far.However, I'm struggling with my attempt to loop through entries in my db and render a <canvas> for each one.
Say this is my data (shortened for brevity):
var paintings = [
{ "_id" : ObjectId("31c75"), "data" : "0,0,0,0" },
{ "_id" : ObjectId("deadb"), "data" : "1,3,0,255" }
];
Which is loaded into the controller by a factory:
app.factory('paintings', ['$http', function($http) {
var o = {
paintings: []
};
o.getAll = function() {
return $http.get('/paintings')
.success(function(data) {
angular.copy(data, o.paintings);
});
}
return o;
}]);
I'm wanting to loop through each entry and construct a <canvas> element, then pass that <canvas> element to another object (Grid) with data as an argument, which creates context and draws on that <canvas> based on the data. Simple, right? Unfortunately, I'm at a loss for how to do so and do not have the language with which to ask a more poignant question.I think problems exist in the fact that I am using inline-templates which aren't yet rendered.
This is generally the approach I am currently trying:
HTML:
<div ng-repeat="painting in paintings" some-directive-maybe="bindCanvas(painting._id)">
<canvas id="{{ painting._id }}" width="800" height="400"></canvas>
</div>
JS:
app.controller('PaintingCtrl', [
'$scope',
function($scope) {
$scope.bindCanvas(canvasId) {
var grid = new Grid(document.getElementById(canvasId));
// Have fun with grid
}
}
]);
Help me, StackOverflow. You're my only hope...
var paintings = [
{ "_id" : ObjectId("31c75"), "data" : "0,0,0,0" },
{ "_id" : ObjectId("deadb"), "data" : "1,3,0,255" }
];
paintings should be in an array.
app.controller('PaintingCtrl', [
'$scope',
function($scope) {
$scope.bindCanvas(canvasId) {
var grid = new Grid(document.getElementById(canvasId));
// Have fun with grid
}
//paintings should be on scope for ng-repeat to find it.
// If ng-repeat does not find paintings on scope then it will create a new empty paintings object on scope
$scope.paintings = [
{ _id : "31c75", data : "0,0,0,0" },
{ _id : "deadb", data : "1,3,0,255" }
];
}
]);
Update:
I have created 2 plunkers.
First, plunker just creates canvas elements with static width and height. Number of canvas elements created is based upon number of paintings in painting.json file.
Second, plunker goes a step further and creates canvas elements with dynamic width and height. Number of canvas elements created is based upon number of paintings in painting.json file. Here width and height are based upon data property in paintings.json file.
Hope this helps.
Following code also works for repeat chart on the same page.
<div ng-repeat="a in abc">
<canvas id="pieChart{{a}}" ng-bind = "bindCanvas(a)" ></canvas>
<div>
Add below code in JS file
$scope.abc = [1,2,3];
$scope.bindCanvas = function(i) {
var ctx = document.getElementById("pieChart"+i);
new Chart(ctx,{
type: 'pie',
data: {
labels: ["Tele-conference", "Projector", "Laptop", "Desktop", "Coffee-machine"],
datasets: [{
label: "Population (millions)",
backgroundColor: ["red", "green","blue","violet","yellow"],
data: [200,100,300,400,150]
}]
},
});
}

How add series dynamically in Highcharts without button? (ANGULAR JS)

My issue is very specific. How I can add series dynamically in highcharts, through Angular JS, without button, otherwise, without function click.
This is my controller:
var deserialize = angular.fromJson(data.dataContent); //EspecĂ­fico para el dataContent
for(var i =0; i < deserialize.length; i++){
var url = deserialize[i];
$http.get(url).success(function(data){
var n_scope = [];//NOMBRES PARA LA SERIE
var e_scope = []; //EMPLEADOS
for (var i = 0; i < data.length; i++) {
var nombre_scope = n_scope.push(data[i].nombre);
var empleados_scope = e_scope.push(parseInt(data[i].empleados));
}
var chart = {};
chart.addSeries({
name: n_scope[i],
data: e_scope[i]
});
HERE GOES THE CHART:
$scope.renderChart = {
chart: {
type: typeArray[2]
},
title: {
text: titleArray[2]
},
xAxis:{
categories: yAxisTiArray[2],
title: {
enabled: false
},
labels: {
enabled: false
}
},
yAxis:{
title: {
text: yAxisTiArray[2]
}
},
series: chart,
legend: {
enabled: true
},
credits: {
enabled: false
},
lang: {
printChart: 'Imprimir gráfico',
downloadPNG: 'Descargar en PNG',
downloadJPEG: 'Descargar en JPG',
downloadPDF: 'Descargar en PDF',
downloadSVG: 'Descargar en SVG',
contextButtonTitle: 'EXPORTAR'
}
};
I was taking this fiddle as example: http://jsfiddle.net/engemasa/WEm4F/, but I don't want a button click function, I want that series add it to chart dynamically
You are almost there, just put your code inside success blockof your API (angularjs api call ). here is an example (how I used to plot series on data change)
var metricData = $http.get(url);
metricData.success(function(value) {
var data = value.responseData;
var graph = [];
angular.forEach(data.datatimeseries, function(metric) {
graph.push([ metric.timestamp, metric.value ]);
// Assuming that datatimeseries is the timeseiries
});
var chartX = $('#yourDivId').highcharts();
chartX.addSeries({
id : graph_id, // some id
data : graph
});
setYaxisExtremes(chartX); // must use it to reflect added series
});
**RESOLVED**
Altough have button, I could resolve this issue.
I have created a repository that integrates Angular.js, PHP, and Highcharts, with Materialize.css, adding series dynamically from external JSON.
link: https://github.com/Nullises/DynamicSeriesHighchartsAngular

Categories