Vue.js Realtime Chart with Websocket or Socket.io - javascript

I use vue.js for my project. Also I use websocket for my datas. Every one second come datas. I need to use realtime chart on my project. But I dont find any chart to solve my problem. For example I use apexchart but it have not refresh datas in my chart when new datas come.
my websocket data like:
{
"topic": "2",
"message": "data"
}
my database data like this:
{
"id": "1",
"deviceName": "refrigerator",
"deviceType": "lineChart",
"topic": "1",
"message": "",
},
{
"id": "8",
"deviceName": "refrigerator",
"deviceType": "lineChart",
"topic": "1",
"message": "",
},
I get my datas from database in this code. And I check my topic, is it same or not. if it is same, I put websocket message to json data to see at screen:
let bufferMessage = [];
bufferMessage = jsonFromDatabase;
socket.on("message", (topic, message) => {
bufferMessage.forEach(element => {
if (element.topic == topic) {
element.message = message.toString();
}
});
});
My code is :
<div id="chart">
<apexchart type="line" height="350" :options="chartOptions" :series="$store.state.bufferMessage[index].message"></apexchart>
</div>
<script>
import Vue from "vue";
import ApexCharts from "apexcharts";
import VueApexCharts from "vue-apexcharts";
Vue.component("apexchart", VueApexCharts);
export default {
components: {
apexchart: VueApexCharts,
},
data() {
return {
series: [
{
name: "Desktops",
data: [],
},
],
chartOptions: {
chart: {
height: 350,
type: "line",
zoom: {
enabled: false,
},
},
dataLabels: {
enabled: false,
},
stroke: {
curve: "straight",
},
title: {
text: "Product Trends by Month",
align: "left",
},
grid: {
row: {
colors: ["#f3f3f3", "transparent"],
opacity: 0.5,
},
},
xaxis: {
categories: [],
},
},
}
</script>
but the way there is no any error in this code. I just want to convert this chart to realtime chart for my websocket datas.

Unless OP shows the part in the code of updating the data, I only can offer a reference of the official apexcharts documentation, about updating the data :
import VueApexCharts from 'vue-apexcharts'
export default {
name: 'Vue Chart',
data: function () {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998],
},
},
series: [{
name: 'Vue Chart',
data: [30, 40, 45, 50, 49, 60, 70, 81]
}]
}
},
methods: {
updateChart() {
const max = 90;
const min = 20;
const newData = this.series[0].data.map(() => {
return Math.floor(Math.random() * (max - min + 1)) + min
})
// In the same way, update the series option
this.series = [{
data: newData
}]
}
}
}
As you can see in the example above, by just changing the props, we trigger the update event of ApexCharts. (from the docs)
Sandbox Example https://codesandbox.io/s/50z5wrmp6k

Related

How to create a RealTime ApexChart with vue

I am trying to follow the vue documentation on ApexCharts website.
https://apexcharts.com/vue-chart-demos/line-charts/realtime/
and the way they structured the component isn't practical. and seem like they have some missing methods in the example.
I wish to create a chart as in the example above that fetches data every second (1 second worth data)
<template>
<VueApexCharts
type="line"
height="350"
width="100%"
:options="chartOptions"
:series="series"
ref="chart"
/>
</template>
<script>
import { ref } from "vue";
import VueApexCharts from "vue3-apexcharts";
import { useStore } from "vuex";
export default {
name: "ChartApex",
components: { VueApexCharts },
props: ["session"],
setup(/* props */) {
let store = useStore();
let chartStream = undefined;
let series = [
{
name: "data1",
data: ref([]),
},
{
name: "data2",
data: [],
},
];
let chartOptions = {
chart: {
// id: "realtime",
width: "100%",
height: 350,
type: "line",
animations: {
enabled: true,
easing: "linear",
dynamicAnimation: {
speed: 1000,
},
},
toolbar: {
show: true,
},
zoom: {
enabled: true,
},
},
dataLabels: {
enabled: true,
},
stroke: {
curve: "straight",
},
title: {
text: "Chart",
align: "left",
},
grid: {
row: {
colors: ["#f3f3f3", "transparent"],
opacity: 0.5,
},
},
xaxis: {
type: "numeric",
show: true,
range: 3,
},
yaxis: {
min: -32768,
show: true,
max: 32768,
},
};
return {
chartOptions,
series,
store,
chartStream,
};
},
watch: {
"session.device.isDataStream"(newVal) {
if (newVal) this.startStream();
else this.endStream();
},
},
methods: {
startStream() {
console.log("starting chart stream");
this.chartStream = setInterval(() => {
// Push data
this.series[0].data.value.push(
...this.store.state.session.samples[0].at(-1)
);
}, 1000);
},
endStream() {
console.log("end chart srtrem");
clearInterval(this.chartStream);
},
},
};
</script>
I wish to display a range of 10 seconds on the X-axis (as in the example above).
When getting a new 1 second worth data: i would like my chart to show extra data and move one second ahead (x-axis) so it always remain as 10 seconds in total.
I am fetching data from my store which keeps updating in chunks. unfortunatly it stacks all in one chart that doesn't move. I wish to display one chunk every second.
I would prefer linking my data directly to the store variable. I am not doing this because i would need to use computed method, and i think it's less efficient (is it?)
the store.state.samples[0] is an array of chunks with around 1 second worth of data in each, structured as apex-charts use: [ts, sample] - looks like this:
// samples[0]
[
// Chunk 1 of almost a second
// [0] =>
[
[ // sample
8.75,
0
],
[ // sample
17.5,
-1
],
[ // etc
26.25,
-2
],
[
35,
-3
],...
],
[ // Chunk 2 of almost a second.
[
958.6875,
-12747
],
[
962.375,
-12808
],
[
966.0625,
-12789
],...
]
]
so that basically i push a chunk worth a second to my chart (..every second)
A lot recommend on apex-charts as a go-to charts library for vue, but seems they lack of proper documentation.
-
Here is link to the source code of the sample. Check there how it was built.
To show the range of 10 seconds use the xaxis.range setting:
const TICKINTERVAL = 1000;
const XAXISRANGE = 10000;
const ARRAYLENGTH = XAXISRANGE / TICKINTERVAL + 1;
xaxis: {
type: 'datetime',
range: XAXISRANGE,
position: 'bottom',
To move the X-Axis you should slice you data array, like the resetData() function does:
function resetData(){
data = data.slice(data.length - 10, data.length);
}
I did it with my ARRAYLENGTH constant
data.push(...new Data...);
if (data.length > ARRAYLENGTH) data = data.slice(-ARRAYLENGTH);

Web App crashes when trying to plot a timeline chart in Apexcharts with Vue 3

I have a web application where I am using ApexCharts with Vue 3 to plot some graphics. I didn't have any trouble using the scatter plot, but when I try to plot a timeline like this example of the website, it completely crashes and I don't know why. maybe I am doing something wrong, but I can't see any error. I would appreciate a lot if you can give me some help because it is important!
I attach here the code of the view:
<template>
<apexchart type="rangeBar" height="350" :options="chartOptions" :series="series"></apexchart>
</template>
<script>
import axios from "../../../services/api.js";
export default {
data() {
return {
chartOptions: {
chart: {
type: 'rangeBar'
},
plotOptions: {
bar: {
horizontal: true,
}
},
fill: {
type: 'solid'
},
xaxis: {
type: 'datetime'
},
},
series: [
{
name: 'Prueba',
data: [
{
x: 'Code',
y: [
new Date('2019-03-02').getTime(),
new Date('2019-03-04').getTime()
]
},
{
x: 'Test',
y: [
new Date('2019-03-04').getTime(),
new Date('2019-03-08').getTime()
]
},
{
x: 'Validation',
y: [
new Date('2019-03-08').getTime(),
new Date('2019-03-12').getTime()
]
},
{
x: 'Deployment',
y: [
new Date('2019-03-12').getTime(),
new Date('2019-03-18').getTime()
]
},
]
},
], //end series
}; //end return
}, //end data
}
</script>
<style scoped>
</style>
There is something wrong in library
If you deep clone series it seems to work
<apexchart type="rangeBar" height="350" :options="chartOptions" :series="JSON.parse(JSON.stringify(series))"></apexchart>

Trying to use axios, vuejs and a dummy api for my chartjs and googlecharts (get requests only)

I am trying to use axios in combination with a link containing this data: { "2015": 226, "2016": 152, "2017": 259, "2018": 265, "2019": 275}, written in JSON.
I want to implement those data, the year for example: 2017 and the revenue: 259 in this chart:
//BARCHART
var ctx = document.getElementById("myChart");
var barChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["2016", "2017", "2018", "2019", "2020"],
datasets: [
{
label: 'Vergangenheit', //Vergangenheit = Past
data: [226, 152, 259, 265, 0],
backgroundColor: 'rgba(110, 205, 126, 1)',
borderColor: 'rgba(110, 205, 126, 1)',
borderWidth: 1,
}
]
},
options: {
responsive: true,
responsiveAnimationDuration: 0,
maintainAspectRatio: true,
aspectRatio: 2,
oneResie: null,
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
}
}
});
With the combination of vue and axios a get request should look like this:
var vm = new Vue({
el: '#get',
data: {
messages: [],
message: ""
},
mounted: function () {
this.getMessages(); // get all messages automatically when the page is loaded
},
methods: {
getMessages: function() {
axios.get('(hiddenhttps:/api/GewinnNachJahr')
.then( (res) => {
this.messages = res.data;
console.log(response.data)
})
},
}
});
I don't want any button to press for the get request, it should always get that data when reloading the page. I already tried the a few code snippets from stackoverflow, the official axios github don't help me at all.
So to summarize i want that axios getrequest on my http datas, then saving and sorting this data and implementing it in my chart.js barchart. I think its enough to just working withing my java.js.
I apologize for the time and effort involved. Thank you in advance.
You can implement chart.js into your vue app. I have created this code it should work.
var vm = new Vue({
el: "#get",
data: {
messages: [],
message: "",
labels: [],
data_set: [],
barChart: ""
},
mounted: function() {
var ctx = document.getElementById("myChart");
this.barChart = new Chart(ctx, {
type: "bar",
data: {
labels: this.labels,
datasets: [
{
label: "Vergangenheit", //Vergangenheit = Past
data: this.data_set,
backgroundColor: "rgba(110, 205, 126, 1)",
borderColor: "rgba(110, 205, 126, 1)",
borderWidth: 1
}
]
},
options: {
responsive: true,
responsiveAnimationDuration: 0,
maintainAspectRatio: true,
aspectRatio: 2,
oneResie: null,
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
}
}
});
},
created: function() {
this.getMessages(); // get all messages automatically when the page is loaded
},
methods: {
getMessages: function() {
axios
.get(
"https://webenggroup06ln3.free.beeceptor.com/zalando/api/GewinnNachJahr"
)
.then(res => {
console.log(res.data);
for (let [key, value] of Object.entries(res.data)) {
this.labels.push(key);
this.data_set.push(value);
}
this.$nextTick(function() {
this.barChart.update();
});
});
}
}
});
the for loop splits your key and your value seperate and then it pushes it into the data array. After everything is pushed inside the array the chart just needs to get updated with this.barChart.update()
mounted or created is the correct place to implement bootstrapping logic for this case. However your code has some problems in definitions and typo:
'(hiddenhttps:/api/GewinnNachJahr': the initial parenthesis should be omitted
console.log(response.data): response should become res for matching callback parameter.
See a working example with a dummy api call and how it loads the data:
var vm = new Vue({
el: '#app',
data: {
messages: [],
message: ""
},
mounted() {
this.getMessages(); // get all messages automatically when the page is loaded
},
methods: {
getMessages() {
axios.get('http://dummy.restapiexample.com/api/v1/employees')
.then((res) => {
this.messages = res.data;
console.log(res.data)
})
},
}
});
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="m in messages">{{JSON.stringify(m)}}</li>
</ul>
</div>

Trying to push or add a custom json on a json

Basically, am working with json objects however my skills are not that much because I am still a newbie in javascript and json or object literals. Am trying to achieve where I would like to push or insert a custom json object at the last element of the other xml/json file. Is there any on way how to do this? I've been trying to do it for quite some time now but could not make it work. Any idea how to make it work? because honestly, I don't have any left :-)
I use getJSON to request a JSON from my website. It works great, but I need to somehow insert another custom object literals at the end of the json is it possible?
By the way here is my code.
$(function() {
$.getJSON('https://some_link_from_a_server_that_produces_xml_file_or_json',
function(data) {
//var dataLength = data.length;
fillData();
function fillData() {
var jsonData = [{
"LastModification": "04:27:48",
"Symbol": "EURUSD",
"Bid": '1.20568',
"Ask": "1.21238",
"High": '1.21789',
"Low": '1.19253',
"Direction": "-1",
"InserTime": "\/Date(1358760600163)\/",
"volume": "0"
}];
for (var i = 0; i < jsonData.length; i++) {
data.push([
parseFloat(jsonData[i].Bid),
parseFloat(jsonData[i].High),
parseFloat(jsonData[i].Low),
parseFloat(jsonData[i].Ask),
parseInt(jsonData[i].InserTime.substr(6)),
parseInt(jsonData[i].volume)
]);
}
CreateChart();
} // end of function fillData()
function CreateChart() {
var chart = new Highcharts.stockChart('container2',
{
title: {
text: 'EUR/USD',
floating: true,
align: 'left',
x: 0,
y: 55
},
subtitle: {
text: 'highest: 1.23223 / lowest: 1.21774',
floating: true,
align: 'left',
x: 0,
y: 70
},
xAxis: {
gridLineWidth: 1
},
yAxis: {
gridLineWidth: 1
},
rangeSelector: {
buttons: [
{
type: 'hour',
count: 1,
text: '1h'
}, {
type: 'day',
count: 1,
text: '1D'
}, {
type: 'all',
count: 1,
text: 'All'
}
],
selected: 1,
inputEnabled: true
},
series: [
{
name: 'EURUSD',
type: 'candlestick',
data: data,
tooltip: {
valueDecimals: 5
}
}
]
}); // end of highcharts.stockchart
}
});
});
I think you are making a array of JSON which looks like [{...}, {...}]
However, in your code you are making an array of array which looks like [[...], [...]].
Try :
let data = [];
const func = function(data) {
fillData();
function fillData() {
var jsonData = [{
"LastModification": "04:27:48",
"Symbol": "EURUSD",
"Bid": '1.20568',
"Ask": "1.21238",
"High": '1.21789',
"Low": '1.19253',
"Direction": "-1",
"InserTime": "\/Date(1358760600163)\/",
"volume": "0"
}];
for (var i = 0; i < jsonData.length; i++) {
data.push({
Bid :parseFloat(jsonData[i].Bid),
High : parseFloat(jsonData[i].High),
Low : parseFloat(jsonData[i].Low),
Ask : parseFloat(jsonData[i].Ask),
InserTime : parseInt(jsonData[i].InserTime.substr(6)),
Volume :parseInt(jsonData[i].volume)
});
}
console.log(data);
} // end of function fillData()
}
func(data);

Plot a a json chart with HighCharts & Vue.js

I'm trying to plot some data I have in my database. I'm following this jsfiddle for the structure. However even though I manage to get the data correctly from my API, the chart shows up but no data is plotted.
My app.js looks something like this:
// Load Sessions
var sessions = new Vue({
el: '#sessions',
delimiters: ["v{","}"],
data: { date:'', sessions:'', json:'', timestamp:''},
methods: {
loadSessions: function(){
var vm = this
axios.get('/api/v1/metrics/')
.then(function(response) {
vm.json = response.data
Highcharts.chart('container', {
chart: {
zoomType: 'x'
},
title: {
text: 'Session Over Time'
},
subtitle: {
text: document.ontouchstart === undefined ?
'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in'
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: 'Sessions'
}
},
legend: {
enabled: false
},
plotOptions: {
area: {
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
},
marker: {
radius: 2
},
lineWidth: 1,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
series: [{
type: 'area',
name: 'Sessions',
data: vm.json
}]
});
})
}
}
})
The vm.json file looks like this:
[ { "date": "2017-01-02", "timestamp": 1483401600, "sessions": 1100 }, { "date": "2017-01-03", "timestamp": 1483488000, "sessions": 1159 }, { "date": "2017-01-04", "timestamp": 1483574400, "sessions": 1084 }]
And I load vue in my html with a simple:
<div id='sessions'>
<a class="button is-primary" #click='loadSessions'>Load Sessions</a>
<!-- Just to test that data is loaded correctly from API -->
<ul style="margin-left: 20px;">
<li v-for="item in json">
<b>Date: </b> v{item.date} <br />
<b>Sessions:</b> v{item.sessions} <br />
<b>Timestamp:</b> v{item.timestamp}
</li>
</ul>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
</div>
Now I think that my problem is on formatting the json, isn't it? The on in the jsfiddle example looks a bit different. How can I get my data to show up?
You can convert your data into the format that is shown in the example.
series: [{
type: 'area',
name: 'Sessions',
data: vm.json.map(d => [new Date(d.date).getTime(), d.sessions])
}]
I converted your date properties to Javascript Date objects in order to use getTime because your timestamp property, where ever it came from, is not a proper Javascript timestamp.
Example.

Categories