I'm using ng2-charts render a simple pie chart in my component like so:
<canvas baseChart [data]="chartData" [colors]="chartColors" [options]="{ tooltips: { enabled: false } }"
chartType="pie"></canvas>
In my component class I have this chart directive which is supposed to be populated with chart object. But if I call this.chart.update() after assigning new value to chartData I get an undefined error.
export class TransactionDetailComponent implements OnInit {
#ViewChild(BaseChartDirective) chart: BaseChartDirective;
chartData: number[] = [];
generateChartData() {
//make an api call
this.chartData = [ //new data ];
}
Calling the generateChartData() method works fine the first time but chart does not get updated on subsequent calls. What am I doing wrong here?
Related
I am using Chart.js 3.5 and Vue 3.
I was successfully able to draw a chart, and I am trying to trigger a data change, inside a Vue method. Unfortunately, I encounter the following issue: "Uncaught TypeError: Cannot set property 'fullSize' of undefined".
Edit2: Added a missed }. Code should now be runnable
MyChart.vue:
<template>
<canvas id="chartEl" width="400" height="400" ref="chartEl"></canvas>
<button #click="addData">Update Chart</button>
</template>
<script>
import Chart from 'chart.js/auto';
export default {
name: "Raw",
data() {
return {
chart: null
}
},
methods: {
createChart() {
this.chart= new Chart(this.$refs["chartEl"], {
type: 'doughnut',
data: {
labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'],
datasets: [
{
backgroundColor: [
'#41B883',
'#E46651',
'#00D8FF',
'#DD1B16'
],
data: [100, 20, 80, 20]
}
]
},
options: {
plugins: {}
}
})
},
addData() {
const data = this.chart.data;
if (data.datasets.length > 0) {
data.labels.push('data #' + (data.labels.length + 1));
for (var index = 0; index < data.datasets.length; ++index) {
data.datasets[index].data.push(123);
}
// Edit2: added missed }
this.chart.update(); } // this line seems to cause the error}
}
},
mounted () {
this.createChart()
},
}
</script>
Edit1: Adding the following to the options makes the chart update successfully, but the error is still present and the animation does not work. The chart flickers and displays the final (updated) state. Other animations, such as hiding/showing arcs do not seem to be afected
options: {
responsive: true,
}
Edit3: Adding "maintainAspectRatio:false" option seems to again stop chart from updating (the above mentioned error is still present)
By walking through the debugger, the following function from 'chart.esm.js' seems to be called successfully a few times, and then error out on last call:
beforeUpdate(chart, _args, options) {
const title = map.get(chart); // this returns null, which will cause the next call to error with the above mentioned exception.
layouts.configure(chart, title, options);
title.options = options;
},
//////////////////////
configure(chart, item, options) {
item.fullSize = options.fullSize;
item.position = options.position;
item.weight = options.weight;
},
This may be a stale post but I just spent several hours wrestling with what seems like the same problem. Perhaps this will help you and/or future people with this issue:
Before assigning the Chart object as an attribute of your Vue component, call Object.seal(...) on it.
Eg:
const chartObj = new Chart(...);
Object.seal(chartObj);
this.chart = chartObj;
This is what worked for me. Vue aggressively mutates attributes of objects under its purview to add reactivity, and as near as I can tell, this prevents the internals of Chart from recognising those objects to retrieve their configurations from its internal mapping when needed. Object.seal prevents this by barring the object from having any new attributes added to it. I'm counting on Chart having added all the attributes it needs at init time - if I notice any weird behaviour from this I'll update this post.
1 year later, Alan's answer helps me too, but my code failed when calling chart.destroy().
So I searched and found what seems to be the "vue way" of handling it: markRaw, here is an example using options API:
import { markRaw } from 'vue'
// ...
export default {
// ...
beforeUnmount () {
if (this.chart) {
this.chart.destroy()
}
},
methods: {
createChart() {
const chart = new Chart(this.$refs["chartEl"], {
// ... your chart data and options
})
this.chart = markRaw(chart)
},
addData() {
// ... your update
this.chart.update()
},
},
}
I'm trying to build reactjs application. In this application I want to use chartjs to show line chart by subscribing the Server sent events(SSE).
I want to update line chart when there is new message from SSE. For that I've developed below code but the chart is not updating when there is new message. I'm printing the new message values in console and I can see those messages. But my chart is not updating on real-time bases.
for e.g if there is new message from SSE, the chart needs to be updated.
when I minimize my browser and maximize by browser that chart is updating with pulled messages values.
Once client connect to my server where SSE emit.
Please find screenshots here
Here is the code
let xAxis =[];
let yAxis=[];
class LineChartComponent extends Component
{
constructor(props) {
super(props);
this.state ={
data: []
}
this.eventSource = new EventSource('http://mydomainname:8090/kafka-messages');
}
componentWillMount() {
this.eventSource.onmessage = (e) => this.updateChart(JSON.parse(e.data));
}
updateChart(message){
let trgTS =message.TrgTS;
let value =parseFloat(100.0*message.Attributes[0].V/message.Attributes[1].V).toFixed(2);
xAxis.push(trgTS);
yAxis.push(value)
console.log(trgTS,value); // I can see the new messages values in console
this.setState(Object.assign({}, {
data: {
labels: xAxis,
datasets:[
{
label:'cycle time',
data: yAxis ,
fill: false,
borderColor: "#3cba9f"
}
]
}
}));
}
render()
{
return(
<div>
<Line data = {this.state.data} options = {{ maintainAspectRatio: false }} />
</div>
)
}
}
I am building an app using Vue.js + Chartjs. I am having a problem where I make a http call to a service to get data, parse it, and pass it into my Chartjs component. However, I keep getting the error Cannot read property '_meta' of undefined
Here are the relevant parts of my component:
<template>
<Chartjs :data="chartData" />
</template>
export default {
data () {
return {
chartData: false
}
},
created () {
this.getData()
},
methods: {
getData() {
const opts = {
url: 'some_url',
method: 'get'
}
request.callRoute(opts).then(results => {
this.chartData = results.data
}).catch(err => {
console.log(err)
})
}
},
components: {
Chartjs
}
}
Note - the chart renders fine if I hard code the chartData field with data that comes back from my request. However, it does NOT work if I make a http request first for my data.
Does anyone know what might be happening?
Thanks in advance!
Vue will render the component with the initial chartData (which is a boolean). You should use a v-if or other logic and render Chartjs component when you have the response. For example you can show a loading message/animation while the chartData is false.
How should I refer to the chart in a vue chart instance and destroy it? I've tried:
var chartVue = new Vue({
el: '#chart',
extends: VueChartJs.Line,
data: {
chartData: {...},
options: {...}
},
mounted() {
this.renderMyChart()
},
methods: {
renderMyChart: function() {
this.renderChart(
this.chartData,
this.options
);
}
},
watch: {
chartData: function() {
this._chart.destroy();
this.renderMyChart();
}
}
});
But it complains
TypeError: Cannot read property 'destroy' of undefined
So it seems this._chart is not the right way to refer to the current chart, anybody knows what is the right way to do this? The idea comes from this stack overflow answer.
It seems this._chart has been moved to this._data._chart, so for instance to make the options reactive, add a watcher as follows:
watch: {
options: function() {
this._data._chart.destroy();
this.renderChart(this.chartData, this.options);
}
}
The Chart object accessible via this._chart is not set until after the renderChart method is called. What's most likely happening is that your chartData is somehow being updated before that renderChart method has been called. So, the watcher for chartData is referencing this._chart before it has been defined.
However, vue-chartjs has a baked-in way to re-render the chart when the dependant data change: the reactiveData and reactiveProp mixins. By adding either of these mixins, the changes to the chartData (data-property or prop, respectively) will re-render the chart to reflect the updated data.
You should use the reactiveData mixin instead:
var chartVue = new Vue({
el: '#chart',
extends: VueChartJs.Line,
mixins: [ VueChartJS.mixins.reactiveData ],
data() {
return {
chartData: {...},
options: {...}
}
},
mounted() {
this.renderChart(this.chartData, this.options);
},
});
Here is my ts file. When ever I call tempData() method in another or same typescript it will return proper info. But when I call it from HTML file it returns 0 for this.links.length and also returns wrong ID for this.graph
export class EditRelationColsComponent implements OnInit {
graph: any = new joint.dia.Graph;
constructor(private globalService: GlobalService) {
}
ngOnInit() {
}
tempData(){
console.log(this.graph);
console.log(this.links.length);
}
}
HTML
<button class="diag" pButton type="button" (click)="tempData()" class="ui-button-info" label="Save"></button>
So when I call function from another JS (After I call the loadGraph()), my graph ID is: 307
When I call function from HTMl, my graph ID is: 308. I don't know why it's not looking for proper graph, but instead creating new one?
Too fast close tag "}". Try this.
export class EditRelationColsComponent implements OnInit {
graph: any = new joint.dia.Graph;
constructor(private globalService: GlobalService) {
}
ngOnInit() {
}
tempData(){
console.log(this.graph);
console.log(this.links.length);
}
}