The issue is shown below.
c2chart1 and c2chart1p are identical graph and shares same data. Issue is c2chart1 is getting updated, but not c2chart1p for the second time.
$('#update').bind('click', function() {
c2updateLineGraph(2, [
[0, 105993],
[25, 659727],
[50, 648727],
[75, 636627],
[100, 636627]
]);
c2updateLineGraph(3, [
[0, 115993],
[25, 659727],
[50, 648727],
[75, 336627],
[100, 236627]
]);
setTimeout(function(){
c2updateLineGraph(2, [
[0, 5993],
[25, 659727],
[50, 648727],
[75, 636627],
[100, 63667]
]);
c2updateLineGraph(3, [
[0, 125993],
[25, 259727],
[50, 648727],
[75, 536627],
[100, 236627]
]);
}, 8000);
});
var c2graphdata = [{
name: 'Current year',
data: []
}, {
name: 'Reapair v1',
data: []
}, {
name: 'Repair v2',
data: []
}, {
name: 'Replacement v1',
data: []
}, {
name: 'Replacement v2',
data: []
}, {
name: 'Facelift v1',
data: []
}, {
name: 'Facelift v2',
data: []
}, {
name: 'Reconstruction v1',
data: []
}, {
name: 'Reconstruction v2',
data: []
}];
function c2updateLineGraph(index, data) {
c2chart1.series[index].setData(data, true);
c2chart1p.series[index].setData(data, true);
}
var c2chart1 = Highcharts.chart('container1', {
series: c2graphdata
});
var c2chart1p = Highcharts.chart('container2', {
series: c2graphdata
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container1"></div>
<div id="container2"></div>
<button id="update">Update charts</button>
The problem is using data variable twice in setData() method. Highcharts use this variable as a reference (library doesn't copy this array). Solution is simple, use data.slice():
function c2updateLineGraph(index, data) {
c2chart1.series[index].setData(data.slice(), true);
c2chart1p.series[index].setData(data.slice(), true);
}
Working demo: http://jsfiddle.net/BlackLabel/hhh2zx3w/1/
Well, I made fiddle based on your code.
See this. :)
HighChart updated
I don't know how it works but, both init functions have to separate.
function chart1Update(index, data) {
c2chart1.series[index].setData(data, true);
}
function chart1pUpdate(index, data){
c2chart1p.series[index].setData(data, true);
}
Related
I have looked at various documentation and similar questions on here, but cannot seem to find the particular solution. Apologies if I have missed anything obvious or have repeated this question!
As a bit of background info, I have implemented a graph using the Chart.js plugin and I am trying to pass the required data from a database.
The arrays of data are the following:
loggedIn: [6.3, 2.4, 7.6, 5.4, 9.9, 7.8],
available: [6.7, 2.2, 11.2, 5.5, 10.1, 7.9],
availableForExisting: [7.2, 3.1, 8.2, 5.6, 9.2, 10.2],
My problem is that only one of the line graphs is being update whilst the rest aren't. The full Chart.JS iFrame code is the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.bundle.min.js"></script>
</head>
<body onLoad="ready()">
<canvas id="myChart" width="250" height="200"></canvas>
<script>
var ctx = document.getElementById("myChart");
const loggedIn = [26, 36, 42, 38, 40, 30, 12];
const available = [34, 44, 33, 24, 25, 28, 25];
const availableForExisting = [16, 13, 25, 33, 40, 33, 45];
const years = [1, 2, 3, 4, 5];
var myChart = new Chart(ctx,
{
type: 'line',
data:
{
labels: years,
datasets: [
{
label: 'Start Balance',
data: loggedIn,//[], //start empty
borderColor:
[
'rgba(164,126,44,1.000)'
],
borderWidth: 1
},
{
label: 'Interest',
data: available,//[], //start empty
borderColor:
[
'rgba(5,99,59,1.000)'
],
borderWidth: 1
},
{
label: 'End Balance',
data: availableForExisting,//[], //start empty
borderColor:
[
'rgba(255,148,112,1.000)'
],
borderWidth: 1
}
]
},
options:
{
tooltips:
{
callbacks:
{
label: function(tooltipItem, data)
{
const title = data.labels[tooltipItem.index];
const dataset = data.datasets[tooltipItem.datasetIndex];
const value = dataset.data[tooltipItem.index];
return title + ': ' + Number(value).toFixed(2) + "%";
}
},
},
onClick: handleClick
}
});
window.onmessage = function(event)
{
if (event.data && Array.isArray(event.data))
{
myChart.data.datasets[0].data = event.data[0];
myChart.data.datasets[1].data = event.data[1];
myChart.data.datasets[2].data = event.data[2];
myChart.update();
}
else
{
console.log("HTML Code Element received a generic message:");
console.log(event.data);
}
};
function handleClick(e)
{
var activeBars = myChart.getElementAtEvent(e);
var value = myChart.config.data.datasets[activeBars[0]._datasetIndex].data[activeBars[0]._index];
var label = activeBars[0]._model.label;
window.parent.postMessage(
{
"type": "click",
"label": label,
"value": value
}, "*");
}
function ready()
{
window.parent.postMessage(
{
"type": "ready"
}, "*");
}
</script>
</body>
</html>
I need to display the data in multiple line graphs, however only one is being updated. The way I am passing the data from the frontend is as follows:
let data =
{
loggedIn: [6.3, 2.4, 7.6, 5.4, 9.9, 7.8],
available: [6.7, 2.2, 11.2, 5.5, 10.1, 7.9],
availableForExisting: [7.2, 3.1, 8.2, 5.6, 9.2, 10.2],
};
$w("#html4").postMessage(data);
$w("#html4").onMessage((event) =>
{
if (event.data.type === 'ready')
{
$w("#html4").postMessage(days[year]);
}
});
I'm attempting to integrate ZingChart as a custom component type in GrapesJs. I've followed some examples and have implemented the following plugin.
blocks.js
import { lineChartRef, chartType } from './consts';
export default (editor, opt = {}) => {
const c = opt;
const bm = editor.BlockManager;
if (c.blocks.indexOf(lineChartRef) >= 0) {
bm.add(lineChartRef, {
label: c.labelLineChart,
content: `
<div data-gjs-type="${chartType}" id="myChart"></div>
`
});
}
};
components.js
import { chartType } from './consts';
export default (editor, opt = {}) => {
const domc = editor.DomComponents;
const defaultType = domc.getType('default');
const defaultModel = defaultType.model;
domc.addType(chartType, {
model: defaultModel.extend(
{
defaults: {
...defaultModel.prototype.defaults,
script: function() {
if (typeof zingchart == 'undefined') {
var script = document.createElement('script');
script.src =
'https://cdn.zingchart.com/zingchart.min.js';
document.body.appendChild(script);
}
}
}
},
{
isComponent: el => {
if (
el.getAttribute &&
el.getAttribute('data-gjs-type') === chartType
) {
return {
type: chartType
};
}
}
}
),
view: {
onRender() {
renderZingChart.bind(this)();
}
}
});
function renderZingChart() {
const data = {
type: 'bar',
title: {
text: 'Data Basics',
fontSize: 24
},
legend: {
draggable: true
},
scaleX: {
// Set scale label
label: { text: 'Days' },
// Convert text on scale indices
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
scaleY: {
label: { text: 'Temperature (°F)' }
},
plot: {
animation: {
effect: 'ANIMATION_EXPAND_BOTTOM',
method: 'ANIMATION_STRONG_EASE_OUT',
sequence: 'ANIMATION_BY_NODE',
speed: 275
}
},
series: [
{
// plot 1 values, linear data
values: [23, 20, 27, 29, 25, 17, 15],
text: 'Week 1'
},
{
// plot 2 values, linear data
values: [35, 42, 33, 49, 35, 47, 35],
text: 'Week 2'
},
{
// plot 2 values, linear data
values: [15, 22, 13, 33, 44, 27, 31],
text: 'Week 3'
}
]
};
const chart = {
id: 'myChart',
data
};
zingchart.render(chart);
}
};
index.js
import grapesjs from 'grapesjs';
import loadBlocks from './blocks';
import loadComponents from './components';
import { lineChartRef } from './consts';
export default grapesjs.plugins.add('fndy-charts', (editor, opts = {}) => {
let c = opts;
let defaults = {
blocks: [lineChartRef],
defaultStyle: 1,
labelLineChart: 'Line Chart'
};
// Load defaults
for (let name in defaults) {
if (!(name in c)) c[name] = defaults[name];
}
loadBlocks(editor, c);
loadComponents(editor, c);
});
consts.js
export const lineChartRef = 'line-chart';
export const chartType = 'chart';
When I add the block to the canvas, it renders, but the ZingChart inside does not. Some things I've tried already:
Verify that the ZingChart render function is being called.
Try moving the renderZingChart function call to different component hooks. Specifically, component:mount, view.init(), and view.onRender().
Move the renderZingChart function call to the script function as a script.onload callback. A similar example can be found here: https://grapesjs.com/docs/modules/Components-js.html#basic-scripts. This does render the ZingChart correctly but doesn't feel correct, and does not allow me to pass in parameters since the script function runs outside the scope of GrapesJs.
I'm running out of ideas so any guidance would be great! Thanks!
I'm making a chart component library with echarts, and the approach for rendering the chart would be similar. The only missing thing I see is element's id. It is an attribute that zing uses to render the chart.
I've made a small example which is obviously not production ready because the id of the block is static. This solves specifically the render problem to make the id dynamic you can do it listening to component:add event and add model id as attribute.
const plugin = editor => {
const {
BlockManager: bm
} = editor;
bm.add("mychart", {
label: "Chart",
content: {
tagName: "div",
attributes: {
id: 'myChart'
},
style: {
width: "300px",
height: "300px"
},
script: function() {
const init = () => {
const data = {
type: "bar",
title: {
text: "Data Basics",
fontSize: 24
},
legend: {
draggable: true
},
scaleX: {
// Set scale label
label: {
text: "Days"
},
// Convert text on scale indices
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
scaleY: {
label: {
text: "Temperature (°F)"
}
},
plot: {
animation: {
effect: "ANIMATION_EXPAND_BOTTOM",
method: "ANIMATION_STRONG_EASE_OUT",
sequence: "ANIMATION_BY_NODE",
speed: 275
}
},
series: [{
// plot 1 values, linear data
values: [23, 20, 27, 29, 25, 17, 15],
text: "Week 1"
},
{
// plot 2 values, linear data
values: [35, 42, 33, 49, 35, 47, 35],
text: "Week 2"
},
{
// plot 2 values, linear data
values: [15, 22, 13, 33, 44, 27, 31],
text: "Week 3"
}
]
};
const chart = {
id: this.id,
data
};
zingchart.render(chart);
};
if (typeof zingchart == "undefined") {
var script = document.createElement("script");
script.onload = init;
script.src = "https://cdn.zingchart.com/zingchart.min.js";
document.body.appendChild(script);
} else {
init();
}
}
}
});
};
const editor = grapesjs.init({
container: "#gjs",
fromElement: true,
height: "100vh",
width: "auto",
storageManager: false,
panels: {
defaults: []
},
plugins: ["gjs-preset-webpage", plugin]
});
You can give a check here the chart is rendering.
Codepen
Hope that's enough, cheers!
I don't think you need to write very complicated code for using Zing charts.I will add a small sample code for making a chart block element , So when you drag and drop the block element then it will make the chart a part of the gjs div of grapesjs .I am using Highcharts.
editor.BlockManager.add('Blockhighcharts', {
label: 'Highchart',
category: 'CHART',
attributes: { class:'gjs-fonts gjs-f-b1' },
content: {
script: function () {
var container = "container"+Math.floor(Math.random() * 100);
$(this).attr("id",container);
$('#gridly_div').append($(this));
var myChart = Highcharts.chart(container, {
chart: {
type: 'bar',
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
});
The HTML code where the chart will be displayed is as follows.
<div id="gjs" style="height:0px; overflow:hidden;">
<style>
#gjs{
height: 100%;
width: 100%;
margin: 0;
}
</style>
<div id='gridly_div' class="gridly">
</div>
Is there a way to have multiple colors?
Example:
Thank you!
New answer:
My original answer bothered me and I thought there must be a better way to achieve this style. So here's a much better solution that uses a radial gradient.
Note that this implementation is quite naïve in that it only supports a single dataset!
const colours = [
{ primary: '#fec1c6', shadow: '#e8b0b5' },
{ primary: '#bdeeed', shadow: '#aad2d0' },
{ primary: '#e4da84', shadow: '#d3ca76' }
];
new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
datasets: [{
data: [3, 2, 2]
}]
},
options: {
cutoutPercentage: 65
},
plugins: [{
beforeDatasetsUpdate: c => {
const x = (c.chartArea.right + c.chartArea.left) / 2,
y = (c.chartArea.bottom + c.chartArea.top) / 2,
bgc = [];
for (let i = 0; i < colours.length; i++) {
const gradient = c.ctx.createRadialGradient(x, y, c.innerRadius, x, y, c.outerRadius);
gradient.addColorStop(0, colours[i].shadow);
gradient.addColorStop(.4, colours[i].shadow);
gradient.addColorStop(.45, colours[i].primary);
gradient.addColorStop(1, colours[i].primary);
bgc.push(gradient);
}
c.config.data.datasets[0].backgroundColor = bgc;
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="75"></canvas>
Original answer:
If you don't mind a 'dirty' solution you can achieve a similar visual result by duplicating your dataset, e.g.:
const values = [3, 2, 2],
primaryColours = ['#fec1c6', '#bdeeed', '#e4da84'],
secondaryColours = ['#e8b0b5', '#aad2d0', '#d3ca76'];
new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
datasets: [{
data: values,
weight: 2,
backgroundColor: primaryColours,
borderColor: primaryColours
}, {
data: values,
weight: 1,
backgroundColor: secondaryColours,
borderColor: secondaryColours
}]
},
options: {
cutoutPercentage: 65
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="75"></canvas>
Following is my javascript Code, but the only thing really relevant is the last function. I want to update the Chart to add another dataset, without reloading the Page. But for reason the added dataset is always undefined. The commented-out line, which uses the exact same array of data, on the other hand works. Since I'm new to javascript I'm not sure, if I missed something obvious, or if chart.js just doesn't support this kind of thing at all.
const CHART = document.getElementById("lineChart");
var dts1 = [
{
label: "Abfall gesamt",
data: Abfall_gesamt,
}
];
var dts2 = [
{
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
}
];
var lineChart = new Chart(CHART, {
type: 'line',
data: {
labels: Jahr,
datasets: dts1
}
});
function myFunction(){
//lineChart.data.datasets[0].data = Abfall_schadstoff;
lineChart.data.datasets.push(dts2);
lineChart.update();
}
The issue is, you are defining your datasets (dts1 and dts2) as an array. They should be an object, like so ...
var dts1 = {
label: "Abfall gesamt",
data: Abfall_gesamt,
};
var dts2 = {
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
};
and then, when generating the chart, set datasets value as ...
datasets: [dts1]
ᴅᴇᴍᴏ
const CHART = document.getElementById("lineChart");
var Abfall_gesamt = [1, 2, 3];
var Abfall_schadstoff = [4, 5, 6]
var dts1 = {
label: "Abfall gesamt",
data: Abfall_gesamt,
backgroundColor: 'rgba(255, 0, 0, 0.2)'
};
var dts2 = {
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
backgroundColor: 'rgba(0, 0, 255, 0.2)'
};
var lineChart = new Chart(CHART, {
type: 'line',
data: {
labels: ['Jahr', 'Mahr', 'Kadr'],
datasets: [dts1]
},
options: {
responsive: false,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 1
}
}]
}
}
});
function myFunction() {
//lineChart.data.datasets[0].data = Abfall_schadstoff;
lineChart.data.datasets.push(dts2);
lineChart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<button id="add" onclick="myFunction()">Add Dataset</button>
<canvas id="lineChart" height="190"></canvas>
So without jquery I want to update highcharts with new data live. I have a chart that displays data from a database, and I am doing a http get request to get the data every few seconds. I am able to grab the data correctly, but when I push the new data onto the series variable for the chart, the graph doesn't update in real time. It only updates when I refresh. How can I fix this? I am using highcharts in angularjs.
you should call series.addPoint() instead of just updating the data array
please see here http://jsfiddle.net/9m3fg/1
js:
var myapp = angular.module('myapp', ["highcharts-ng"]);
myapp.controller('myctrl', function ($scope) {
$scope.addPoints = function () {
var seriesArray = $scope.chartConfig.series
var newValue = Math.floor((Math.random() * 10) + 1);
$scope.chartConfig.xAxis.currentMax++;
//if you've got one series push new value to that series
seriesArray[0].data.push(newValue);
};
$scope.chartConfig = {
options: {
chart: {
type: 'line',
zoomType: 'x'
}
},
series: [{
data: [10, 15, 12, 8, 7, 1, 1, 19, 15, 10]
}],
title: {
text: 'Hello'
},
xAxis: {
currentMin: 0,
currentMax: 10,
minRange: 1
},
loading: false
}
});
From your code it looks like you want to add new series rather then new data if yes please see here: http://jsfiddle.net/bYx4a/
var app = angular.module('app', ["highcharts-ng"]);
app.controller("myCtrl", ['$scope', '$http', function ($scope, $http) {
var count = 0;
$scope.chartOptions = {
chart: {
type: 'line'
},
title: {
text: 'Fruit Consumption'
},
xAxis: {
categories: ['Apples', 'Bananas', 'Oranges']
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: 'Jane',
data: [1, 0, 4]
}]
};
$scope.addSeries = function () {
var newData = {
name: 'John',
data: [1, 4, 3]
};
$scope.chartOptions.series.push({
name: newData.name,
data: newData.data
})
};
}]);
Here is my solution for using Highcharts addPoint function in the highcharts-ng directive:
$scope.chart_realtimeForceConfig = {
options: {
chart: {
type: 'line',
},
plotOptions: {
series: {
animation: false
},
},
},
series: [
{
name: 'Fx',
data: []
},
],
func: function(chart) {
$timeout(function() {
chart.reflow();
$scope.highchart = chart;
}, 300);
socket.on('ati_sensordata', function(data) {
if (data) {
var splited = data.split('|');
if (splited.length >= 6) {
var val = parseFloat(splited[5]);
var shift = chart.series[0].data.length > 100;
chart.series[0].addPoint(val, true, shift, false);
}
}
});
},
loading: false
}