Return value from dataPointSelection event in Apexcharts - vue.js

I have a question related to this question and this answer but they don't solve my question completely. I'm using vue and apexcharts and I would like to return a value or update a variable from an event. Is it possible to return something instead of printing it in the console?
Something like this:
events: {
dataPointSelection: function (event, chartContext, config) {
this.active = this.series[config.seriesIndex];
}
}
The problem that I face is that "this" makes reference to the overall vue component and therefore "series" and "active" cannot be found.
Here is the code that gives me"TypeError: this.series is undefined" when I click on a point data. The series data I get from the parent component and it looks like this:
[{"name":"S-1","data":[[2.65,100], [6.67,100]]}, {"name":"S-2","data":[[0,50],[2.65,50]]}]
<script>
import VueApexCharts from 'vue-apexcharts';
export default {
name: "myGraph",
components: {
apexchart: VueApexCharts,
},
props: {
series: {}
},
data: () => ({
active: undefined,
chartOptions: {
chart: {
width: '100%',
animations: {
enabled: false
},
events: {
dataPointSelection: function (event, chartContext, config) {
this.active = this.series[config.seriesIndex];
}
}
},
tooltip: {
intersect: true,
shared: false
},
markers: {size: 1},
}
}),
}
}
</script>
The idea is that on dataPointSelection, it should activate that serie in order to access later on other information that will be store in that object.

The easiest way is to bind the event directly in the component
<apexchart type="bar" #dataPointSelection="dataPointSelectionHandler"></apexchart>
methods: {
dataPointSelectionHandler(e, chartContext, config) {
console.log(chartContext, config)
}
}
Another way is to use ES6 arrow functions in your chart configuration
computed: {
chartOptions: function() {
return {
chart: {
events: {
dataPointSelection: (e, chart, opts) => {
// you can call Vue methods now as "this" will point to the Vue instance when you use ES6 arrow function
this.VueDemoMethod();
}
}
},
}
}
}

I think this is simply what you are looking for
chart: {
type: 'area',
events: {
dataPointSelection(event, chartContext, config) {
console.log(config.config.series[config.seriesIndex])
console.log(config.config.series[config.seriesIndex].name)
console.log(config.config.series[config.seriesIndex].data[config.dataPointIndex])
}
}
}
if you need by the click, this is better
chart: {
type: 'area',
events: {
click(event, chartContext, config) {
console.log(config.config.series[config.seriesIndex])
console.log(config.config.series[config.seriesIndex].name)
console.log(config.config.series[config.seriesIndex].data[config.dataPointIndex])
}
}
}
source How to access value on dataPointSelection function of Apexchart
documentation events https://apexcharts.com/docs/options/chart/events/

Related

Vuejs 3 - Unable to remove resizeEvent

I have resize event in my mounted() works well.
data() {
return {
...
eventHandler: null,
};
},
mounted() {
this.eventHandler = window.addEventListener("resize", () => {
console.log("resize");
});
},
but when I tried to remove the resize listener on unmount()
beforeUnmount() {
console.log("unmonunted");
window.removeEventListener("resize", this.eventHandler);
},
the resize event is still firing
Anyone know how to solve this?
window.addEventListener returns undefined, not the event handler function.
You should change your code like this:
data() {
return {};
},
mounted() {
window.addEventListener("resize", this.eventHandler);
},
beforeUnmount() {
window.removeEventListener("resize", this.eventHandler);
},
methods: {
eventHandler() {
console.log("resize");
}
}

Change data between few router-view

I have two components, and can't pass data from one to another component
at first router-view have
data() {
return {
mode: true,
}
},
<input type="checkbox" class="switch-mode" v-model="mode" #change="$root.$emit('switch-mode', mode)">
and other is
data() {
return {
filter: {
mode: false,
order: 'DESC'
},
}
},
mounted() {
this.$root.$on('switch-mode', function (EventGrid) {
console.log('Grid mode is '+EventGrid); //this works it return true,false
this.filter.mode = EventGrid; // not working this.filter is undefined"
})
},
This happens because your function doesn't know what this is, you should explicitly tell it to use your component:
mounted() {
this.$root.$on(
'switch-mode',
(function (EventGrid) { ... }).bind(this)
)
}
Or, more effectively and modern, use an arrow function:
mounted() {
this.$root.$on('switch-mode', (EventGrid) => { ... })
}

VueJS - vue-charts.js

I am trying to pass data I fetch from API to vue-chartjs as props, I am doing as in the documentation but it does not work.
Main component
<monthly-price-chart :chartdata="chartdata"/>
import MonthlyPriceChart from './charts/MonthlyPriceChart'
export default {
data(){
return {
chartdata: {
labels: [],
datasets: [
{
label: 'Total price',
data: []
}
]
},
options: {
responsive: true,
maintainAspectRatio: false
}
}
},
components: {
MonthlyPriceChart
},
created() {
axios.get('/api/stats/monthly')
.then(response => {
let rides = response.data
forEach(rides, (ride) => {
this.chartdata.labels.push(ride.month)
this.chartdata.datasets[0].data.push(ride.total_price)
})
})
.catch(error => {
console.log(error)
})
}
}
In response I have an array of obejcts, each of which looks like this:
{
month: "2018-10",
total_distance: 40,
total_price: 119.95
}
Then I want to send the data somehow to the chart so I push the months to chartdata.labels and total_price to chartdata.datasets[0].data.
chart component
import { Bar } from 'vue-chartjs'
export default {
extends: Bar,
props: {
chartdata: {
type: Array | Object,
required: false
}
},
mounted () {
console.log(this.chartdata)
this.renderChart(this.chartdata, this.options)
}
}
console.log(this.chartdata) outputs my chartsdata object from my main component and the data is there so the data is passed correctly to chart but nothing is rendered on the chart.
The documentation says this:
<script>
import LineChart from './LineChart.vue'
export default {
name: 'LineChartContainer',
components: { LineChart },
data: () => ({
loaded: false,
chartdata: null
}),
async mounted () {
this.loaded = false
try {
const { userlist } = await fetch('/api/userlist')
this.chartData = userlist
this.loaded = true
} catch (e) {
console.error(e)
}
}
}
</script>
I find this documentation a bit vague because it does not explain what I need to pass in chartdatato the chart as props. Can you help me?
Your issue is that API requests are async. So it happens that your chart will be rendered, before your API request finishes. A common pattern is to use a loading state and v-if.
There is an example in the docs: https://vue-chartjs.org/guide/#chart-with-api-data

change VueJS component data value from inside Highchart event

I'm using vue2-highcharts to build a pie chart. In my component, which contains the HighCharts chart, there is a Boolean variable named showOverlay. I'm trying to change the showOverlay value when a HighCharts click event occurs.
The component code is:
<template>
<section class="charts">
<vue-highcharts :options="pieOptions" ref="pieChart"></vue-highcharts>
</section>
</template>
<script>
/* eslint-disable */
import VueHighcharts from 'vue2-highcharts'
export default {
components: {
VueHighcharts
},
props: ['score', 'show'],
data() {
return {
showOverlay: false,
pieOptions:
{
chart: {
type: "pie",
options3d: {
enabled: false,
alpha: 45
}
},
plotOptions: {
pie: {
innerSize: 100,
depth: 45
},
series: {
cursor: 'pointer',
point: {
events: {
click: function (e) {
// ----- HERE I WANT TO SET showOverlay -----
// ----- for example: this.showOverlay = false -----
alert('Category: ' + this.name + ', value: ' + this.y);
}
}
}
}
},
series: [
{
name: "Platform Score",
data: [
["Spotify", 3],
["Deezer", 1]
]
}
]
}
}
},
methods: {
}
}
</script>
As you can see, I marked in the code where I want to change the showOverlay value, but this holds the HighCharts instance at that line, and I can't figure out how to access the Vue instance to change the showOverlay value.
Worth mentioning: the final goal is to $emit the change to the parent component. I found a relevant suggestion in another post, moving the data setup into the mounted hook and using an arrow-function:
mounted () {
const that = this;
Highcharts.mapChart(this.$el, {
series: [{
events: {
click: () => {
that.$emit('somethingHappened', 'someData');
}
}
}]
})
}
but when I tried it with a bit of modification:
mounted () {
const that = this;
this.$refs.pieChart.chart(this.$el, {
series: [{
events: {
click: () => {
that.$emit('somethingHappened', 'someData')
}
}
}]
})
},
I got the following error:
this.$refs.pieChart.chart is not a function
How can I tackle this?
Inside your component's data, changing pieOptions.plotOptions.series.point.events.click to an arrow-function would provide the Vue instance as this inside the handler. The HighCharts series point (previously this in your click-handler) is stored in the event argument as point, so your pieOptions Vue data should look something like this:
click: ({point}) => {
this.showOverlay = false;
alert('Category: ' + point.name + ', value: ' + point.y);
this.$emit('somethingHappened', 'someData');
}
demo

VueJS: How to access computed values in render function

I currently have a component with this render function:
render(createElement, context) {
return createElement(
'div', {
'class': 'sliced'
},
[
createElement('div', {
'class' : 'sliced-inner',
'style' : context.style
}
)
]
)
},
and I've added functional: true. The "style" is a computed value, but it doesn't seem to get passed with the context object. Is there any way to access computed values in a Vue render function?
A functional component has no state, so a computed property is redundant. In the following example I'm creating a header component that toggles between foo and bar when clicked:
Vue.component('message', {
render (createElement) {
return createElement('h1', {
on: {
click: event => {
return this.foo = !this.foo
}
}
}, this.fooBar)
},
computed: {
fooBar() {
return (this.foo) ? 'foo' : 'bar'
}
},
data(){
return {
foo: true
}
}
});
As you can see the header value is based on a computed, and it works fine because it is not a functional component so can have state: https://jsfiddle.net/hwbbukvd/
If I make that a functional component by adding functional: true, then it does not work because it's display relies on the component having state: https://jsfiddle.net/cygjdjru/
See: https://v2.vuejs.org/v2/guide/render-function.html#Functional-Components
In your case, if you aren't looking for style to be reactive, then I'm guessing you just want to pass a prop
Vue.component('message', {
functional: true,
render(createElement, context) {
return createElement('h1', context.props.foo)
},
props: {
foo: {
required: true
}
}
});
See: https://jsfiddle.net/qhzh0a2c/