I am trying to update highcharts data from a vuex-store, but the issue is that when the click event is triggered, the data in the vuex store state is mutated, but it only reflects in my highcharts component when i make some changes to the code and save the changes.
I am working on a dashboard using Vue and Highcharts.
<template>
<div>
<vue-highcharts :options="options" ref="lineCharts"></vue-highcharts>
<v-btn>{{parts}}</v-btn>
</div>
</template>
<script>
import VueHighcharts from 'vue2-highcharts';
import Vue from 'vue';
export default {
components: {
VueHighcharts,
},
data() {
return {
options: {
chart: {
type: 'spline',
title: 'Hassaan',
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
yAxis: {
title: {
text: '',
},
labels: {
formatter() {
return `${this.value}°`;
},
},
},
tooltip: {
crosshairs: true,
shared: true,
},
credits: {
enabled: false,
},
plotOptions: {
spline: {
marker: {
radius: 4,
lineColor: '#666666',
lineWidth: 1,
},
},
},
series: [],
},
};
},
created() {
Vue.set(this.options, 'series', this.$store.state.parts);
},
};
</script>
I want the data to be updated without making any changes to the code and saving it.
You should use a computed to get the value with reactivity from the store, that way you don't need the created hook anymore. Also you should never access values from your store directly through the state, instead create a getter.
I'm not sure what are you trying to do, however this should be the right structure. If you only want to set this.options.series = this.$store.getters.parts. Like you are already doing with the Vue.set(this.options, 'series', this.$store.state.parts), in that case, add a watcher for the computed property and set the new property value.
{
watch: {
parts (updatedParts) {
this.series.parts = updatedParts;
}
},
computed: {
parts () {
return this.$store.getters.parts;
}
}
}
Related
I have a problem with the chart!
How can I enlarge and fixed the point I clicked on?
An example of my chart below, I will be grateful for the help
example: http://jsfiddle.net/4bounhdg/
$(function () {
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
plotOptions: {
series: {
cursor: 'pointer',
marker: {
enabled: true,
symbol: 'circle',
radius: 6},
point: {
events: {
click: function() {
for (var i = 0; i < this.series.data.length; i++) {
this.series.data[i].update({ color: '#7db7ed' }, true, false);
}
this.update({ color: '#0053ff'}, true, false);
}
}
}
}
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0]
}]
});
});
You can set the higher radius in the update callback.
Demo: http://jsfiddle.net/BlackLabel/rkqtcps8/
point: {
events: {
click: function() {
this.update({
color: '#0053ff',
marker: {
radius: 20
}
}, true, false);
}
}
}
API: https://api.highcharts.com/class-reference/Highcharts.Point#update
I am trying to do communication between two components using $emit and $on:
I am unable to communicated between the two components and unable to update highcharts-chart in component-B from click event in component-A.
JavaScript Code for Component-A:
import Vue from 'vue';
const bus = new Vue();
const pause = ms => new Promise(resolve => setTimeout(resolve, ms));
export default {
data: () => ({
active: [],
avatar: null,
open: [],
users: [],
}),
computed: {
items() {
return [
{
name: 'Users',
children: this.users,
},
];
},
selected() {
if (!this.active.length) return undefined;
const id = this.active[0];
return this.users.find(user => user.id === id);
},
},
methods: {
fetchData() {
const id = this.active[0];
this.parts = this.users.find(user => user.id === id);
bus.$emit('new_parts', this.parts.data);
console.log(this.parts.data);
},
async fetchUsers(item) {
// Remove in 6 months and say
// you've made optimizations! :)
await pause(1500);
return fetch('http://localhost:8081/api/toppartsdata')
.then(res => res.json())
.then(json => (item.children.push(...json)))
.catch(err => console.warn(err));
},
},
};
JavaScript Code for Component-B:
import VueHighcharts from 'vue2-highcharts';
import Vue from 'vue';
const bus = new Vue();
const asyncData = {
name: 'Prediction Chart',
marker: {
symbol: 'circle',
},
data: [],
};
export default {
components: {
VueHighcharts,
},
data() {
return {
options: {
chart: {
type: 'spline',
title: '',
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
yAxis: {
title: {
text: 'LINECOST',
},
labels: {
formatter() {
return `${this.value}°`;
},
},
},
tooltip: {
crosshairs: true,
shared: true,
},
credits: {
enabled: false,
},
plotOptions: {
spline: {
marker: {
radius: 4,
lineColor: '#666666',
lineWidth: 1,
},
},
},
series: [],
},
};
},
methods: {
test() {
// eslint-disable-next-line func-names
bus.$on('new_parts', (data) => {
alert(value);
});
},
load() {
// eslint-disable-next-line func-names
bus.$on('new_parts', function (data) {
this.asyncData.data = data;
});
const { lineCharts } = this.$refs;
lineCharts.delegateMethod('showLoading', 'Loading...');
setTimeout(() => {
lineCharts.addSeries(asyncData.data);
lineCharts.hideLoading();
}, 2000);
},
},
};
I want to be able to update my highcharts timeline graph using click events from component-A and update the data coming from the event to component-B everytime click the new button.
If this is the real code you're using in both components, then it doesn't work because you're creating 2 different buses instead of reusing the same bus for events.
Try pulling it out in a separate file, such as bus.js, then export it and import it in the components where you need to interact with it:
// bus.js
export default new Vue()
// Component A
import bus from './bus'
bus.$emit('new_parts', this.parts.data)
// Component B
import bus from './bus'
bus.$on('new_parts', (data) => {
alert(value);
})
Let me know if something doesn't make sense.
The easiest way to handle is to use the this.$root to emit and listen to the event:
To emit the event from component-a:
this.$root.$emit('new_parts', this.parts.data)
To listen to the event on component b:
mounted() {
this.$root.$on('new_parts', (data) => {
//Your code here
});
},
Please add the onclick in the mounted method.
Here is a good article on events on Vue: https://flaviocopes.com/vue-components-communication/
Below is the updated code for my highcharts component:
<template>
<div>
<vue-highcharts :options="options" ref="lineCharts"></vue-highcharts>
</div>
</template>
<script>
import VueHighcharts from 'vue2-highcharts';
import { bus } from '../../../main';
export default {
props: {
partsdata: {
type: Array,
},
},
components: {
VueHighcharts,
},
data() {
return {
options: {
chart: {
type: 'spline',
title: 'Hassaan',
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
yAxis: {
title: {
text: '',
},
labels: {
formatter() {
return `${this.value}°`;
},
},
},
tooltip: {
crosshairs: true,
shared: true,
},
credits: {
enabled: false,
},
plotOptions: {
spline: {
marker: {
radius: 4,
lineColor: '#666666',
lineWidth: 1,
},
},
},
series: [],
},
};
},
created() {
bus.$on('new_user', (data) => { this.series = data; });
},
};
</script>
Your last code is almost correct, however here is the wrong part:
Wrong code:
created() {
bus.$on('new_user', (data) => { this.series = data; });
}
this.series is not referring to series data, instead, you're adding a new property to the whole component object. In your case it should look like that:
Correct code:
created() {
bus.$on('new_user', (data) => { this.options.series[0].data = data; });
}
I prepared you an online example using highcharts-vue official Highcharts wrapper for VUE which I recommend you. There you will find working code with communication between components.
Demo:
https://codesandbox.io/s/r0qmqjljwq
I am trying to create a stacked bar chart using Highcharts.
I also want to display data labels in the bars, but I only want to display the labels if the bars are long enough to contain them. If the text is longer than the bar, I want to hide the text
Here is an example of the chart I am trying to build:
Highcharts.chart('container', {
chart: {
type: 'bar'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
plotOptions: {
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
formatter: function(){
return 'text for value '+this.point.y;
}
}
}
},
series: [{
data: [1,2,3,4,5,6,7,8,9,10,11,12]
},{
data: [12,11,10,9,8,7,6,5,4,3,2,1]
}]
});
And here is the jsfiddle link: http://jsfiddle.net/xyszd7p4/
How can I hide the data points if they are too long? In my example, I want to hide the "text for value 1" data label because it does not fit inside the bar.
This answer shows one way of hiding labels that are too long: https://stackoverflow.com/a/49750334. That does not work directly for a bar chart (or for multiple series). But by editing it a little, like this:
chart: {
events: {
load: function() {
this.series.forEach(function(series) {
var points = series.points
points.forEach(function(point) {
console.log(point);
if (point.shapeArgs.height < point.dataLabel.width) { //using shapeArgs.height since this is a bar chart (would be width otherwise)
point.dataLabel.hide();
}
});
});
}
},
type: 'bar'
},
You can acheive what you are after.
Working JSFiddle example: http://jsfiddle.net/ewolden/87c2t4uy/
I want to render a histogram/line chart using HighCharts.
I don't want to hard code the array which is used by series.
My data that I wish to render is in the object display, which looks like:
0: o, 107983,
1: 1, 347923,
2: 2, 182329,
.
.
.
My code is here:
function RenderChart(display) {
myDisplay = display;
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line'
},
title: {
text: 'Metric histogram'
},
xAxis: {
//categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
minPadding: 0.05,
maxPadding: 0.05
},
plotOptions: {
line: {
animation: false
},
column: {
groupPadding: 0,
pointPadding: 0,
borderWidth: 0
}
},
series: [{
data: [myDisplay]
}]
});
};
This doesn't render the line chart. It renders an empty chart.
run a routine such that the response you get is processed as accepted by the highchart. then you can assign the resultant to the data directly.
try this, it will work
as i am trying with above displayed code, but unable to get data values over bar chart
i want data values to be displayed over bars. when i tried it by adding datalabels value get displayed in the bar not on the bar .please help in this regard.
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Monthly Average Rainfall'
},
subtitle: {
text: 'Source: WorldClimate.com'
},
xAxis: {
categories: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
},
yAxis: {
min: 0,
title: {
text: 'Rainfall (mm)'
}
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:.1f} mm</b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: [{
name: 'Tokyo',
data: [49.9]
}, {
name: 'New York',
data: [83.6]
}, {
name: 'London',
data: [48.9]
}, {
name: 'Berlin',
data: [42.4]
}]
});
});
Just set dataLabels: { enabled: true } under plotOptions. See: http://jsfiddle.net/3bQne/499/