How to use custom data in my chart on jsreport? - javascript

I am trying to make some charts with custom data in jsreport and using Chart.js, the problem is that i don't know how to use custom data to fill my chart with. So far, i created a very big json with my data and the function to generate the chart and place inside a canvas, but i can't call the function inside my html with the handlebars because it says the document is not defined. So, how can i use my data to create my charts and display it inside a canvas?
P.S.: I can easily display a chart with static data, but i really want to do this using the json that i created.
My function to create my chart:
function graficoEstiloAdaptado(exame){
var ctx = document.getElementById('graficoEsquerdo').getContext('2d');
var total = 280;
var incentivador = 0;
var idealizador = 0;
var detalhista = 0;
var sociavel = 0;
for(var i=0;i<exame.respostas.length;i++){
for(var j=0;j<exame.respostas[i].alternativas.length;j++){
switch(exame.respostas[i].alternativas[j].categoria){
case 'Incentivador':
incentivador += 4-j;
break;
case 'Idealizador':
idealizador += 4-j;
break;
case 'Detalhista':
detalhista += 4-j;
break;
case 'Sociável':
sociavel += 4-j;
break;
}
}
}
var porcentagens = {
incentivador: (incentivador/total).toFixed(1),
idealizador: (idealizador/total).toFixed(1),
detalhista: (detalhista/total).toFixed(1),
sociavel: (sociavel/total).toFixed(1)
};
var chartEstiloAdaptado = new Chart(ctx, {
type: 'bar',
data: {
labels: [porcentagens.incentivador + "%", porcentagens.idealizador + "%", porcentagens.detalhista + "%", porcentagens.sociavel + "%"],
datasets: [{
label: "Gráfico I",
data: [
porcentagens.incentivador,
porcentagens.idealizador,
porcentagens.detalhista,
porcentagens.sociavel
]
}]
},
options: {
animation: {
onComplete: function() {
window.JSREPORT_READY_TO_START = true;
}
}
}
});
}
And i don't want to use an API to get the data yet, i just want to structure the report the way i like and after that use an API to fetch the data.

The main idea is described in this blog:
Define helper function which makes JSON string from the parameter
function toJSON(data) {
return JSON.stringify(data);
}
And call this helper in inline script
<script>
var data= {{{toJSON this}}}
</script>
The full example with chart.js can look like this
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js'></script>
</head>
<body>
<canvas id='myChart' style="margin-top:30px"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
datasets: [{
label: 'apples',
data: {{{toJSON A}}},
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'oranges',
data: {{{toJSON B}}},
backgroundColor: "rgba(255,153,0,0.4)"
}]
},
options: {
animation: {
onComplete: function () {
// set the PDF printing trigger when the animation is done
// to have this working, the phantom-pdf menu in the left must
// have the wait for printing trigger option selected
window.JSREPORT_READY_TO_START = true
}
}
}
});
</script>
</body>
</html>
Working playground demo can be found here.

Related

How to display two C3 charts in the same row in two different divs

I've some problems with c3 plugins.
I'm trying to put 2 charts in a structure like this:
<div class="row">
<div class="col-6">
<div id="chart1"></div>
</div>
<div class="col-6">
<div id="chart2"></div>
</div>
</div>
My output is the attached one, and i couldn't find the reason why the charts go out of the div.
I've already tried to use chart.resize() but it doesn't work (maybe i put it in the wrong place).
Can you help me ?
You can find my code here:
js1, js2, html
Thank you !
The problem is that you are loading the charts (I think!) within a div that is not displayed when the page loads, the C3 doesn't know how to size the charts correctly.
Instead of loading every chart in the Document Ready, wrap your posts in a function like this:
function loadStatArticoliCharts() {
$.post(
'{{ url('myGetter') }}/{{ data.listId }}',
{},
function(data) {
grafico_fatturato = c3.generate({
bindto: "#fatturato-mensile-barre",
data: {
columns: [
[new Date().getFullYear() - 1, 0,0,0,0,0,0,0,0,0,0,0,0],
[new Date().getFullYear(), 0,0,0,0,0,0,0,0,0,0,0,0],
],
type : 'bar',
colors: data.colors
},
bar: {
width: 30
},
axis: {
x: {
type: 'category',
categories: months
},
y: {
tick: {
format: function(value) { return value.formatMoney(2, ',', '.') }
}
}
},
tooltip: {
format: {
value: function(value) { return "€ " + value.formatMoney(2, ",", "."); }
}
},
transition: {
duration: 1000
}
});
setTimeout(function() {
grafico_fatturato.load({
columns: [
data.columns.current,
data.columns.past
],
});
grafico_fatturato.resize();
}, 500);
}
);
}
Create a global boolean variable to store if you've already loaded the charts (so you won't trigger the load multiple times) with
let loadedChart1 = false;
let loadedChart2 = false;
Finally create a controller that will trigger the load function when you click the tab:
$('a[data-toggle="tab"]').on('shown.bs.tab', function (ev) {
let tabId = $(ev.target).attr("aria-controls");
switch (tabId) {
case "chart1":
if (!loadedChart1) {
loadChart1();
loadedChart1 = true;
}
break;
case "chart2":
if (!loadedChart2) {
loadChart2();
loadedChart2 = true;
}
break;
}
let oldTabId = $(ev.relatedTarget).attr("aria-controls");
$('#' + oldTabId).removeClass("active");
}

Show multiple charts at HTML page?

I need to show multiple charts using chart.js.
I know how many charts I need only after the user sends the data so I can't write different canvas-id at the HTML file(i don't know how many I need).
So my question is how can I show a number of charts without know it at the beginning? (every chart is a different row at the matrix)
my code:
<HTML>
<div class="chart-container">
<div class="pie-chart-container">
<canvas id="pie-chartcanvas-1"></canvas>
</div>
</div>
<!-- javascript -->
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
javascript
var piechart = $("#pie-chartcanvas-1");
var data1 = {
labels: itemsArr,
datasets: [
{
label: "Population (millions)",
backgroundColor:
["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850","#3cba9f"],
data: mat[0]
}
]
};
var chart = new Chart(piechart,{
type:"pie",
data : data1,
options:{
title: {
display: true,
text: namesArr[0]
}
}});
Is the data the user sends you as the data1 object? Do you always need a pie chart? If so...
function createChart(data, id) {
addCanvas(id); // some id generated by you or sent by the user
generateChart(data, id); // data from the user
}
function addCanvas(id) { // create the new canvas
let canvas = document.createElement("canvas");
canv.setAttribute("id", "canvasID");
document.getElementsByClassName('pie-chart-container')[0].appendChild(canvas);
}
function generateChart(data, id) { // initialize the new chart
let piechart = $("#" + id);
let chart = new Chart(piechart,{
type:"pie",
data : data,
options: {
title: {
display: true,
text: namesArr[0]
}
}
});
}

Using data from API with Chart JS

I am getting data from an api and then reformatting part of it into an array using .map(), I am successfully able to do this, but when it comes time to pass it into Chart JS as data it does work. I am able to pass in a normal, hard coded, array but not my own data...
I tried using an Angular directive (NG2-Charts) to help out thinking maybe that was the problem, but that doesn't work either...
Component.ts:
... Other variable and stuff up here...
getStockData() {
this.stocksService.getStockData()
.subscribe(
(response) => {
for(var i = 0; i < response.length; i++) {
this.stockOpen.push(response[i]['open']);
}
console.log('after loop: ', this.stockOpen);
},
(error) => console.error(error)
);
console.log('real: ', this.stockOpen);
console.log('test: ', this.testData);
}
// Chart JS version
buildStockChart() {
var ctx = document.querySelector("#chart");
this.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [1,2,3,4,5],
datasets: [
{
data: this.stockOpen,
borderColor: "#3cba9f",
fill: false
}
]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [{
display: true
}],
yAxes: [{
display: true
}],
}
}
});
}
// NG2-Charts version
public lineChartData:Array<any> = [
{data: this.testData},
];
public lineChartLabels:Array<any> = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
public lineChartOptions:any = {
responsive: true
};
Result from console.log():
i also have same problem with chart JS on angular so i force to use another chart.
im now using angular 2 chart js.
i think the problem here is the delay of data fetch by API, the CHART component is already render on html view but the data is still not fetch by the API service.
try to add this code on your code block. This will handle the data if API service data is available.
()=>{this.buildStockChart();}
this.stocksService.getStockData()
.subscribe(
(response) => {
for(var i = 0; i < response.length; i++) {
this.stockOpen.push(response[i]['open']);
}
console.log('after loop: ', this.stockOpen);
},
()=>{
this.buildStockChart();
}
);
console.log('real: ', this.stockOpen);
console.log('test: ', this.testData);
}
This chart is easy to manage for dynamic instances.
Hope this chart will work on you.
https://www.npmjs.com/package/angular2-chartjs
When are you calling the buildStockChart() method?
You should call it right after the for loop into the callback you pass to the subscribe method, since that's the moment when this.stockOpen is populated (before that moment it will be empty as you are seeing in the console).
As #Joseph Agbing, I was unable to get it work with angular 7. I'm now using chart.js only
npm install chart.js --save
with into my someChart.component.html
<div style="display: block"><!--Mandatory div including chart-->
<canvas id="canvas">{{chart}}</canvas>
</div>
into my someChart.component.ts
called from my httpClient.post(...).subscribe(lData => (setChartDataFromEntities(lDataProcessed), ...)
import { Chart } from 'chart.js';
export class someClass {
/**
*
* #param aDate
* #param aChargeUnitArray
*/
setChartDataFromEntities( aDate: Date, aChargeUnitArray: ChargeUnit[] ){
console.debug('setChartDataFromEntities->', aChargeUnitArray)
let lChartDataArray = []
let lChartDataLineDataArray: Array<Number> = []
let lChartLabelsArray: string[] = []
let l_s: string
aChargeUnitArray.forEach(element => {
lChartDataLineDataArray.push(element.charge)
lChartLabelsArray.push(MiscHelper.dateTimeHMSForChart(element.timestamp))
});
lChartDataArray.push(
{
data: lChartDataLineDataArray,
label: MiscHelper.dateForGui(aDate),
}
)
this.chart = new Chart('canvas', {
type: 'line',
data: {
labels: lChartLabelsArray,
datasets: lChartDataArray
},
options: {
legend: {
display: false
},
scales: {
xAxes: [{
display: true
}],
yAxes: [{
display: true
}],
}
}
});
this.statusMessage = 'Chart loaded'
}
hope it helps somebody more than the day I wasted trying to get it work...

ZingChart X-axis labels showing as numbers instead of strings

I am using the ZingChart library to graph results from an API call. When I pass in a normal array for the "values" field of the chart data object, everything works fine. However, when I pass in an array made from Object.keys(titleSet) (where titleSet is a normal Javascript object), the graph displays as follows:
Example Chart
As you can see, the x-axis is now labeled with numbers instead of the array of strings. But when I print out the the result of Object.keys(titleSet) vs. passing in a normal array, they both appear to be the same in the console. Can anyone help me figure out what I'm doing wrong?
//List of movies inputted by the user
var movieList = [];
var movieSet = {};
var IMDBData = {
"values": [],
"text": "IMDB",
};
var metascoreData = {
"values": [],
"text": "Metascore"
};
var RTMData = {
"values": [],
"text": "Rotten Tomatoes Meter"
};
var RTUData = {
"values": [],
"text": "Rotten Tomatoes User"
};
var chartData = {
"type":"bar",
"legend":{
"adjust-layout": true
},
"plotarea": {
"adjust-layout":true
},
"plot":{
"stacked": true,
"border-radius": "1px",
"tooltip": {
"text": "Rated %v by %plot-text"
},
"animation":{
"effect":"11",
"method":"3",
"sequence":"ANIMATION_BY_PLOT_AND_NODE",
"speed":10
}
},
"scale-x": {
"label":{ /* Scale Title */
"text":"Movie Title",
},
"values": Object.keys(movieSet) /* Needs to be list of movie titles */
},
"scale-y": {
"label":{ /* Scale Title */
"text":"Total Score",
}
},
"series":[metascoreData, IMDBData, RTUData, RTMData]
};
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
Full Disclosure, I'm a member of the ZingChart team.
Thank you for updating your question. The problem is you have defined your variable movieSet before the variablechartData. When parsing the page, top down, it is executing Object.keys({}) on an empty object when creating the variable chartData. You should just directly assign it into your config later on chartData['scale-x']['values'] = Object.keys(moviSet).
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
There is a problem with the above code as well. It seems you are calling render on the chart every time you call this API. You should have one initial zingchart.render() and then from there on out use our API. I would suggest setdata method as it replaces a whole new JSON packet or modify method.
I am making some assumptions on how you are handling data. Regardless, check out the following demo
var movieValues = {};
var myConfig = {
type: "bar",
scaleX:{
values:[]
},
series : [
{
values : [35,42,67,89,25,34,67,85]
}
]
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 300,
width: '100%'
});
var callback = function(data) {
movieValues[data.title] = true;
myConfig.scaleX.values = Object.keys(movieValues);
zingchart.exec('myChart', 'setdata', {
data:myConfig
})
}
var index = 0;
var movieNamesFromDB = ['Sausage Party', 'Animal House', 'Hot Rod', 'Blazing Saddles'];
setInterval(function() {
if (index < 4) {
callback({title:movieNamesFromDB[index++]});
}
},1000)
<!DOCTYPE html>
<html>
<head>
<!--Assets will be injected here on compile. Use the assets button above-->
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
<script> zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
</script>
<!--Inject End-->
</head>
<body>
<div id='myChart'></div>
</body>
</html>
If you noticed in the demo, the length of scaleX.values determines how many nodes are shown on the graph. If you change values to labels this wont happen.

Trying to iterate an array through mutliple highcharts

So I have a large amount of data that I need to display all stored in separate CSV files. So I created two charts just fine in highcharts, one line, one area, but instead of copying and pasting the function over and over again I was hoping I could just iterate through it like so:
var library = ['data/data.csv', 'data/attendanceGroup.csv'];
var libraryLength = library.length;
var area =['#attendanceRoom','#attendanceGroup'];
var i = 0;
function areaChart(){
$(function () {
$.get(library[i], function(csv) {
$(area[i]).highcharts({
chart: {
type: 'area'
},
data: {
csv: csv
},
title: {
text: 'Attendance by Room'
},
yAxis: {
title: {
text: null
},
minorTickInterval: 'auto'
},
legend:{
align: 'left',
verticalAlign: 'top',
floating: true
},
});
});
});
}
for (i = 0; i < libraryLength; i++){
areaChart();
}
I was looking at this Manage multiple highchart charts in a single webpage using jQuery.extend() or Highcharts.setOptions but that sets options for each individual chart and then you just make them over and over again. I thought a better solution might be to just have the one function and then just re-run it for each individual chart especially since I'm pulling the data from .CSV files.
So is this possible? Or should I go with jQuery.extend()?
Thanks for any help in advance!
Just two things I would improve:
$(function () { }); - I would encapsulate whole JS, not only part with AJAX and Highcharts:
$(function () {
var library = ['data/data.csv', 'data/attendanceGroup.csv'];
...
for (i = 0; i < libraryLength; i++){
areaChart();
}
});
make library[i] and area[i] as arguments for areaChart():
$(function () {
var library = ['data/data.csv', 'data/attendanceGroup.csv'];
...
function areaChart(lib, area){
$.get(lib, function(csv) {
$(area).highcharts({
chart: {
type: 'area'
},
data: {
csv: csv
}
});
});
}
for (i = 0; i < libraryLength; i++){
areaChart(library[i], area[i]);
}
});
Of course, you can add more params to areaChart for example type, and pass on what kind of the chart should be rendered:
$(function () {
var library = ['data/data.csv', 'data/attendanceGroup.csv'];
var types = ['line', 'area'];
...
function areaChart(lib, area, type){
$.get(lib, function(csv) {
$(area).highcharts({
chart: {
type: type
},
data: {
csv: csv
}
});
});
}
for (i = 0; i < libraryLength; i++){
areaChart(library[i], area[i], types[i]);
}
});
Don't overdo with the params, no one likes to read 10params and control order etc. Instead you may consider passing one object param (renamed from areaChart to myChart):
myChart({
lib: library[i],
area: area[i],
type: types[i]
});
And in myChart() method:
function myChart(options) {
$.get(options.lib, function(csv) {
$(options.area).highcharts({
chart: {
type: options.type
},
data: {
csv: csv
}
});
});
}

Categories

Resources