Vuejs Component Communication - highcharts

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

Related

Custom hover / tooltip data on highcharts network chart

I am trying to display a custom tooltip on a react highcharts network chart that includes the node id as well as the title and other fields in the JSON data I am feeding it. However I am not able to get this to work using the formatted function specified in the API.
My simplified code is as follows:
import React, { useState, useEffect, useRef } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import networkgraph from "highcharts/modules/networkgraph";
networkgraph(Highcharts);
const NetworkChart = () => {
const chartComponent = useRef(null); // reference to chart obj
const [nodeData, setNodeData] = useState([
{ 'id': 'A', title:'ABC', other:'X', dataLabels: { enabled: true }, marker: { radius: 11, fillColor: 'red' } },
{ 'id': 'P', title:'CDE', other:'Y', dataLabels: { enabled: true } },
{ 'id': 'K', title:'EDF', other:'X', dataLabels: { enabled: true } },
{ 'id': 'S', title:'DFG', other:'Z', dataLabels: { enabled: true } },
{ 'id': 'D', title:'SDF', other:'Y', dataLabels: { enabled: true } },
]);
const [linkData, setLinkData] = useState([
{ 'from': 'D', 'to': 'A' },
{ 'from': 'S', 'to': 'A' },
{ 'from': 'K', 'to': 'A' },
{ 'from': 'P', 'to': 'A' }
]);
const [chartOptions, setChartOptions] = useState({
chart: {
type: 'networkgraph',
plotBorderWidth: 1,
},
title: {
text: 'Phrasal verbs'
},
subtitle: {
text: 'Integration: ' + 'euler'
},
credits: false,
plotOptions: {
networkgraph: {
layoutAlgorithm: {
enableSimulation: false,
integration: 'euler',
linkLength: 25,
},
keys: ['from', 'to'],
marker: {
radius: 5,
lineWidth: 1
}
},
series: {
point: {
events: {
click: function () {
// click function
},
}
}
}
},
series: [{
allowPointSelect: true,
nodes: nodeData,
data: linkData,
}]
});
return (
<div>
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
containerProps={{ style: { height: 700 } }}
allowChartUpdate = {true}
ref={chartComponent}
/>
</div>
)
};
export default NetworkChart;
Currently all I see is node id on hover. What I want to see is node id, title and other field values when I hover on each node in the chart.
You can get the required propeerties through: this.point.options
tooltip: {
formatter: function() {
const { title, other } = this.point.options;
return 'Title: ' + title + ' Other: ' + other
}
}
Live demo: http://jsfiddle.net/BlackLabel/4zgrnqc2/
API Reference: https://api.highcharts.com/highcharts/tooltip.formatter

Vuejs Data Update

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;
}
}
}

How to open multiple level drilldown in highcharts programatically?

I have this code for my chart
angular.module('myModule', []).service("CompOffLeaveService", function ($http) {
this.getdata = function () {
return $http({
method: "post",
url: "/Dashboard/GetCompOffLeaveReport",
params: [{ YearID: $("#YearIn option:selected").text() }],
dataType: "json"
});
};
}).controller('myController', function (CompOffLeaveService) {
GetAlldata();
function GetAlldata() {
var getCompOffLeaveData = CompOffLeaveService.getdata();
getCompOffLeaveData.then(function (NoOfLeaves) {
var myData = new Array();
for (var j = 0; j < NoOfLeaves.data.TeamNames.length; j++) {
debugger;
if (NoOfLeaves.data.TeamNames[j] != null) {
myData[j] = [
NoOfLeaves.data.TeamNames[j],
NoOfLeaves.data.TeamWiseCompOffLeaves[j]
];
}
else { break; }
}
var mySeries = [];
for (var i = 0; i < myData.length; i++) {
mySeries.push({ name: myData[i][0], y: myData[i][1], drilldown: true });
}
Highcharts.chart('container', {
chart: {
type: 'column'
},
credits: {
enabled: false
},
title: {
text: 'Comp Off Leave Report' + ' ' + $("#YearIn option:selected").text()
},
xAxis: {
type: 'category'
},
yAxis: {
title: {
text: 'Total CompOffLeave Recorded'
}
},
legend: {
enabled: false
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
format: '{point.y}COL'
}
}
},
tooltip: {
headerFormat: '<span style="font-size:11px">{series.name}</span><br>',
pointFormat: '<span style="color:{point.color}">{point.name}</span>: <b>{point.y} COL</b> in total<br/>',
},
series: [{
name: 'Team',
colorByPoint: true,
data: mySeries
}],
drilldown: {
series: []
}
});
}, function () {
alert('Error in getting records');
});
} $("#btnLoad").click(function (event) {
GetAlldata();
});
$("#btnBack").click(function (event) {
window.location.href = "home";
});
$(function () {
$("#draggable").draggable({ axis: "y", containment: "#parent", scroll: false, revert: true });
$("#resizable").resizable();
});
});
it is working perfectly well and gives me this output
what I want is to open a drilldown on any column click which will be having all months of the year e.g:
and on further drilldown I want this.e.g:
I can pass the data then..but drilldown is not getting open.

Can we set bar labels with dynamic custom array in Highcharts?

I think this is bug in the library.
I want a custom labels on each bar, but it is not working!
var options = {
chart: {
type: 'column'
},
plotOptions: {
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
formatter: function() {
if (this.series.options.myLabels) {
return "XXX: " + this.series.options.myLabels[this.point.x];
}
}
}
}
},
exporting: {
url: 'http://export.highcharts.com/',
chartOptions: {
yAxis: {
labels: {
style: {
color: 'green'
}
}
}
}
},
yAxis: {
labels: {
style: {
color: 'red'
},
format: '{value:,.0f}'
}
},
series: [{
type: 'column',
data: [10000, 20000, 8000],
myLabels: ["A", "B", "C"]
}, {
type: 'column',
data: [10000, 2000, 10000]
}, {
type: 'spline',
data: [5000, 15000, 5000]
}
]
};
var globals = {
lang: {
thousandsSep: ','
}
}
$('#export').click(function() {
var obj = {},
exportUrl = options.exporting.url;
obj.options = JSON.stringify(options);
obj.globaloptions = JSON.stringify(globals);
obj.type = 'image/png';
obj.async = true;
$.ajax({
type: 'post',
url: exportUrl,
data: obj,
success: function(data) {
var imgContainer = $("#imgContainer");
$('<img>').attr('src', exportUrl + data).attr('width', '450px').appendTo(imgContainer);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="export">get image</button>
<div id="imgContainer"></div>
JSON is not a bug, because you cannot place the function inside the json. Functions are not executable and are skipped. So you need to use the callback and there place your function in series.update();
Fixed demo:
- http://jsfiddle.net/ayyunrb4/

Highcharts and json format

I have this code for a query in a file called querytojson.php:
if(!$query = #pg_query("SELECT AVG(\"UploadSpeed\") AS \"UploadSpeed\",
AVG(\"DownloadSpeed\") AS \"DownloadSpeed\",
AVG(\"Latency\") AS \"Latency\",
AVG(\"Jitter\") AS \"Jitter\",
AVG(\"PacketLoss\") AS \"PacketLoss\" FROM \"ZipPerformance\" "))
die("<br>Errore nella query: " . pg_last_error($query));
while($row = pg_fetch_assoc($query)){
// aggiungo all'array
$risultati[] = $row;
}
// stampo a video i risultati formattati secondo la sintassi di JSON
echo json_encode($risultati);
the json data is in this format:
[{"UploadSpeed":"0.342153197182936","DownloadSpeed":"4.35602301750153","Latency":"110.290067528565","Jitter":"0.0333323723888251","PacketLoss":"0.164373075044556"}]
Now I'd want to create a graph with Highcharts library like this, the file is called index1.html:
$(document).ready(function() {
var options = {
chart: {
renderTo: 'container',
defaultSeriesType: 'column'
},
title: {
text: 'HOBBIT'
},
tooltip: {
},
labels: {
html: 'index.html'
},
xAxis: {
categories: []
},
yAxis: {
title: {
text: 'Velocità di connessione'
}
},
series: []
};
})
I'd want to pass json data directly to index.html.
I've done somthing similar to this. The way i did it was I created the chart in a sperate js file and just called the chart as a function passing in the JSON object as a variable. This is an example of one of my scripts in use on my site. In your case the labels and values are the same variable so the loop iteration is different, but the idea is still the same.
var chart;
function pieChart(id, values, labels, animate){
if (animate === undefined ){
animate = true;
}
var arrays = new Array();
for (i=0;i<values.length;i++) {
arrays[i] = [labels[i], values[i]];
}
chart = new Highcharts.Chart({
chart: {
renderTo: id,
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false
},
credits: {
enabled: false
},
title: {
text: 'Event Occurrence',
style: {
color: '#000000'
}
},
tooltip: {
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ this.y +' %';
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ this.y +' %';
}
}
},
series: {
animation: animate
}
},
series: [{
type: 'pie',
name: 'Probability',
data: arrays
}]
});
}

Resources