I'm trying to create an ant design chart with some custom labels on the bars based on what data they contain. Is it possible to do this dynamically? I want the labels to say "Water" and "Land", which is the type of each respective data. Here is my code which I'm playing around in the Ant Design Charts Sandbox
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Bar } from '#ant-design/plots';
const DemoBar = () => {
const data = [
{
year: '1991',
value: 3,
type: 'Water',
},
{
year: '1991',
value: 4,
type: 'Land',
},
];
const config = {
data: data.reverse(),
isStack: true,
xField: 'value',
yField: 'year',
seriesField: 'type',
legend: false,
label: {content: data[0].type}, // How can the content link to the data?
interactions: [{ type: 'tooltip', enable: false }]
};
return <Bar {...config} />;
};
ReactDOM.render(<DemoBar />, document.getElementById('container'));
I figured it out: Javascript lets you create a function here. Replacing the label line with this line solved it:
label: {content: ({ type }) => { return type },},
For typescript the line is:
label: { content: ({ type }: any) => { return type }, },
Related
In my project I wish to toggle between two sets of data: total hours worked each day in current week, and total hours worked each month in the current year. I am able to achieve Chart.js label manipulation in my button handlers, but I am unable to modify the state's data property. What am I missing here? Thank you for helping.
Note: The commented code produce this error statement when uncommented:
Uncaught TypeError: nextDatasets.map is not a function
LineChart.js
const [userData, setUserData] = useState({
labels: Object.keys(daysData),
datasets: [
{
label: "Hours worked",
data: Object.values(daysData),
backgroundColor: "red",
borderColor: "black",
borderWidth: 2,
},
],
});
const onShowWeeklyHandler = () => {
setUserData({ labels: Object.keys(daysData) });
// setUserData({ datasets: { data: Object.keys(daysData) } });
};
const onShowMonthlyHandler = () => {
setUserData({ labels: Object.keys(monthsData) });
// setUserData({ datasets: { data: Object.keys(monthsData) } });
};
You may be encountering issues because you are trying to shallow merge state with the useState hook. React does not support this. See this answer for more info.
If that does not solve your issue, have you looked into react-chartjs-2? You might be able to achieve what you want doing something like the following.
In your parent component (the one that contains the chart):
import { Bar } from "react-chartjs-2"; // import whatever chart type you need
...
const [userData, setUserData] = useState({
labels: Object.keys(daysData),
datasets: [
{
label: "Hours worked",
data: Object.values(daysData),
backgroundColor: "red",
borderColor: "black",
borderWidth: 2,
},
],
});
Then your handlers can be:
const onShowWeeklyHandler = () => {
setUserData({
...userData,
datasets: {
...userData.datasets,
labels: Object.keys(daysData),
data: Object.values(daysData),
},
});
};
const onShowMonthlyHandler = () => {
setUserData({
...userData,
datasets: {
...userData.datasets,
labels: Object.keys(monthsData),
data: Object.values(monthsData),
},
});
};
Finally:
return <Bar data={userData.datasets} />; // adapt for whatever chart type you need
When I want to use react chartjs it is giving me these errors
The version I use for chart.js is ^3.5.0 and ^4.0.1 for react-chartjs-2
I downgrade it to version 2 but it didn't work
Chart component codes
import React from 'react'
import { Bar } from 'react-chartjs-2'
import { Chart as ChartJS } from 'chart.js/auto'
function Chart({ chartData }) {
return <Bar data={chartData} />
}
export default Chart
Main component
const [data, setData] = useState({
labels: UserData.map((data) => data.year),
datasets: [
{
label: 'Users Gained',
data: UserData.map((data) => data.userGain),
},
],
})
<Chart chartData={data} />
Data
export const UserData = [
{
id: 1,
year: 2016,
userGain: 80000,
userLost: 823,
},
{
id: 2,
year: 2017,
userGain: 45677,
userLost: 345,
},
{
id: 3,
year: 2018,
userGain: 78888,
userLost: 555,
},
{
id: 4,
year: 2019,
userGain: 90000,
userLost: 4555,
},
{
id: 5,
year: 2020,
userGain: 4300,
userLost: 234,
},
]
in Main component:
import { UserData } from './UserData.js';
import React from 'react'
const [data, setData] = React.useState({
labels: UserData?.map((data) => data.year),
datasets: [
{
label: 'Users Gained',
data: UserData?.map((data) => data.userGain),
},
],
})
<Chart chartData={data} />
The problem may be related to the way you import/read the UserData. Assuming the data is defined in UserData.js, you could import it as follows:
import { UserData } from './UserData.js';
Please take a look at this StackBlitz and see how it could work with your code.
I am trying to display a graph using the library Chart.js.
My data are in the format:
data: [["15-11-2019", 25], ["16-11-2019", 35], ["17-11-2019", 40], ["18-11-2019", 20], ["19-11-2019", 15]]
I am not able to transform the x-Axis in a time-axis.
I tried to pass in the options parameter the time type but it does not seem to work.
The graph renders, but the x-axis does not exists, only the y-axis exists.
import React, {Component, useState, useEffect, useCallback} from 'react';
import Plot from 'react-plotly.js';
import { Chart } from 'react-charts'
function MyChart() {
const data = React.useMemo(
() => [
{
label: 'Series 1',
data: [["15-11-2019", 25], ["16-11-2019", 35], ["17-11-2019",40], ["18-11-2019", 20], ["19-11-2019", 15]]
}],
[]
)
const axes = React.useMemo(
() => [
{ primary: true, type: 'linear', position: 'bottom' },
{ type: 'linear', position: 'left' },
],
[]
)
const options = React.useMemo(
() => [
{
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'day'
}
}]
}
}
],
[]
)
return (
// A react-chart hyper-responsively and continuusly fills the available
// space of its parent element automatically
<div
style={{
width: '600px',
height: '500px'
}}
>
<Chart data={data} axes={axes} options={options}/>
</div>
)
}
export default MyChart;
Update: See this specific line about format in the documentation
The x-axis data points may additionally be specified via the t or x attribute when using the time scale.
See more here https://www.chartjs.org/docs/latest/axes/cartesian/time.html
Your datafield must be a dictionary/object of x, y values:
data: [{x: "2019-01-03", y: 15}, {x: "2019-01-04", y: 18}, ...]
When creating your chart, set type in options.scales:
...,
options: {
scales: {
xAxes: [{
type: 'time',
}]
},
...
I am using HighCharts for the first time and it's proving to be difficult to find sources to help me with this problem.
I am trying to display multiple charts with different numbers of series. Rather than set up multiple options templates I want to use just one and use a function to customize the options object each time. Here I've provided mock data for what one of the charts will look like as received from the API. In the application I plan on feeding it from HTML.
I keep running into various problems due to my lack of understanding of HighCharts, right now I am just going around in circles fixing one problem only for a past problem to pop-up again I was hoping someone can point me in the right direction.
This is my Class:
export class CompletenessTabComponent implements OnInit, AfterViewInit {
#ViewChild('chartTarget') chartTarget: ElementRef;
chart: Highcharts.ChartObject;
dataObj = {"id":0,
"tableName":"d1 vs d2",
"data":{"d1Count":[50,45,55,60,70],
"d2Count":[40,32,55,58,33],
"Errors":[2,3,4,1,0]},
"dates":[20180927,20180928,20181001,20181002,20181003]
};
constructor() { }
setHighChartOptions(data, seriesNames, chartTitle ): any {
count = 1;
highChartOptions.title.text = chartTitle;
highChartOptions.xAxis.data = data.dates;
this.chart.series[0].setData(this.dataObj.data[0]);
Object.keys(data.data).forEach((key) =>
this.chart.addSeries({
name: seriesNames[count],
data: data.data[key],
type: 'line'
}) count ++
);
return highChartOptions;
}
getHighChartOptions(){
return highChartOptions;
}
ngOnInit() {
this.chart = chart(this.chartTarget.nativeElement, this.setHighChartOptions(this.dataObj, ['d1', 'd2', 'error'], this.dataObj.tableName));
Object.keys(this.dataObj.data).forEach(key =>
console.log(key, this.dataObj.data[key]))
}
ngAfterViewInit() {
}
}
const highChartOptions = {
colors:
['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'],
credits: {
enabled: false
},
chart: {
backgroundColor: null,
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
itemStyle: {
fontSize: '10px',
fontWeight: 'normal'
}
},
tooltip: {
valuePrefix: '$'
},
title: {
text: ''
},
xAxis: {
type: 'date',
data: [],
title: 'date'
},
yAxis: {
title: {
align: 'middle',
rotation: 0,
text: ''
}
},
plotOptions: {
series: {
marker: {
enabled: false
},
shadow: false
}
},
series: [{
type: 'line',
name: '',
data: [null]
}]
};
A few errors I keep receiving are:
in setHighChartOptions() : Cannot read property 'series' of undefined (at, this.chart.series[0]), & cannot read 'keys' of undefined (at the start of the ForEach loop).
I believe the big issue is the chart object is undefined which doesn't make sense since I've instantiated it at the top of the class and then onInit I set the chart options.
I hope you will be able spot my errors and assist me. Thank You.
Most probably, it's because you're trying to refer to series which is not already defined. Actually, if you calling the setHighchartsOptions for a first time, your chart is not defined yet.
In order to make it works, please try to init the chart first (even could be the empty chart, without any data), and then call another methods on Chart object, like addSeries. Something like that, but i didn't saw whole your app, so it's hard to write completely adjusted solution.
ngOnInit() {
this.chart = chart(this.chartTarget.nativeElement, {});
this.chart.update(this.setHighChartOptions(this.dataObj, ['d1', 'd2', 'error'], this.dataObj.tableName))
Object.keys(this.dataObj.data).forEach(key =>
console.log(key, this.dataObj.data[key]))
}
First of all, you must be careful to use import correctly all js package in your Angular project:
import { chart } from 'highcharts';
import * as Highcharts from 'highcharts';
Also, you must check does your package installed correctly with "npm"?
After all, As my point of view, this example can help you to better understand how to use "highcharts" package in your project, other configs depend on the version of "highcharts" you are using it.
import { Component, ElementRef, ViewChild } from '#angular/core';
import { chart } from 'highcharts';
import * as Highcharts from 'highcharts';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Highcharts Sample';
#ViewChild('chartTarget') chartTarget: ElementRef;
chart: Highcharts.ChartObject;
ngAfterViewInit() {
const options: Highcharts.Options = {
chart: {
type: 'bar'
},
title: {
text: 'Sample Title'
},
xAxis: {
categories: ['Top Test', 'Test 2', 'Test3']
},
yAxis: {
title: {
text: 'Fox Rex'
}
},
series: [{
name: 'Tomi',
data: [1, 0, 4]
}, {
name: 'Alex',
data: [5, 7, 3]
}]
};
this.chart = chart(this.chartTarget.nativeElement, options);
}
addSeries(){
this.chart.addSeries({
name:'Test',
data:[2,3,7]
})
}
}
And HTML template of sample:
<div #chartTarget>
chart target sample
</div>
Also, you must serve data to highcharts as the format is standard for that, check JSON sample data format in examples in highcharts website.
This is my VueJS code, i use this lib https://www.npmjs.com/package/highcharts-vue
So i dont know how i can get data and set it to series before the graph is drawn. Or if this is not posible, how can i redraw graph properly? Becouse now i set some default data, then get data from page, and redraw graph, but when its done and i see my graph, the scrollbar go to the left side and has a very small range. So how set options without change scrollbar and range selector?
<template>
<highcharts :constructor-type="'stockChart'" :options="options" :updateArgs="[true, false]" ref="linerchart"></highcharts>
</template>
<script>
import {Chart} from 'highcharts-vue'
import Highcharts from 'highcharts'
import stockInit from 'highcharts/modules/stock'
stockInit(Highcharts)
export default {
data: () => ({
obj: [],
names: ['CAFF'],
options: {
credits: { enabled: false },
rangeSelector: {
selected: 1,
inputEnabled: false,
buttonTheme: {
visibility: 'visible'
}
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showCheckbox: false,
events: {
checkboxClick: function (event) {
if (event.checked) {
this.show();
} else {
this.hide();
}
}
}
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> USD<br/>',
split: true
},
series: [{
name: "CAFF",
data: [1,2,3,4,5,6]
}]
}
}),
methods: {
linerGraph() {
var that = this;
this.names.forEach(function(name, i){
axios.get('/test/account/CAFF')
.then(response => {
console.log(response.data)
that.obj.push({name: name, data: response.data});
});
});
this.options.series = that.obj
},
},
components: {
highcharts: Chart
},
mounted() {
console.log(this.$refs.linerchart)
this.linerGraph();
}
}
</script>
You should use other VueJS lifecycle hook point to run axios request, because you are trying to download it when the component is already mounted(), so please try to use one of hook points before that one, e.g created().
Here is the Lifecycle diagram from Vue General Documentation: