In my project for my clan, Lords of War on the Supercell games. I am trying to make a chart for the current donations with chart.js. I'm using Vue for the front-end and vue-chartjs for the charts. There is only 1 problem. When i open the page, the datasets are not visible. So how can i fix that?
this is the chart data object:
donation_chart_data: {
labels: [],
datasets: [
{
label: 'Donated',
hidden: false,
backgroundColor: '#1D93D3',
data: []
},
{
label: 'Received',
hidden: false,
backgroundColor: '#C7031F',
data: []
}
]
}
This is the DonationChart component:
import { Bar, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins;
export default {
extends: Bar,
name: 'ClashRoyaleDonationChart',
mixins: [reactiveProp],
mounted () {
// Overwriting base render method with actual data.
this.renderChart(this.chartData,
{
responsive: true,
maintainAspectRatio: false
})
}
}
PS: when I click the legend, the data is displayed properly
Well, I guess you are using an API to get your data?
Then you need to check if the data is available. Axios / Ajax requests are async. So most of the time, your chart will be rendered without data.
Just add a v-if="loaded" on your chart component and in your
axios.get().then() method, set loaded = true and you should be fine.
Related
So I have been trying to make a linechart work with Echarts. I made this LineChart.vue and expect it to get props, which are arrays, from its father component as options data of Echarts.
But the props, which are proxies of arrays, doesn't seem to work well. It is shown in the console that it has the right target, but this proxy is not recognized by Echarts, so there was no data on my chart.
And to make it wierder to me, I accidently found out that if I keep my terminal open, make some changes to the code (which is nothing but comment and uncomment the same lines), and save it (which probably rerends this component), the props somehow works and the linechart actually shows up! But if I refresh the page, the data goes blank again.
Here is my code:
<template>
<div id="chart"></div>
</template>
<script>
let chart;
export default {
data() {
return {
option: {
name: "demo",
xAxis: {
type: "category",
data: [],
},
yAxis: {
// type: "value",
},
series: [
{
data: [],
type: "line",
},
],
},
};
},
props: {
xAxisData: Array,
seriesData: Array,
},
methods: {
initChart() {
chart = this.$echarts.init(document.getElementById("chart"));
// these are the four lines that I commented and uncommented to make things wierd
this.option.xAxis.data = this.xAxisData;
this.option.series[0].data = this.seriesData;
console.log(this.option.xAxis.data);
console.log(this.option.series[0].data);
chart.setOption(this.option);
},
},
mounted() {
this.initChart();
},
watch: {
xAxisData: {
handler: function (newData) {
this.option.xAxis.data = newData;
},
deep: true,
},
seriesData: {
handler: function (newData) {
this.option.series[0].data = newData;
},
deep: true,
},
},
};
</script>
<style scoped>
#chart {
height: 250px;
width: 400px;
}
</style>
And here iswhat is the proxy like before and after I made some minor changes to the code
I also tried to turn this proxy xAxisData into an object using Object.assign(), but it turns out to be empty! I am starting to think that it might have somthing to do with component life cycle, but I have no clue when and where I can get a functional proxy. Can someone tell me what is actually going on?
FYI, here are value of props in console and value of props in vue devtool.
Just figured it out. Just so you know, the info provided above was insufficient, and I made a noob move.
My vue component was fine, it was async request that caused this problem. The data for my Echarts props is requseted through a Axios request, and my child-component (the linechart) was rendered before I got the data. Some how, the proxies of the arrays donot have the data, yet they got the target shown right. By the time my child-component got the right data, the Echart was already rendered with outdated options data, which by the way was empty. And that is why re-render it can show us the data. It has nothing to do with proxy, proxy works just fine. It is me that needs to pay more attention to aysnc movement. Also, I learned that obviously Echarts was not reactive at all, so I watched the props and updated the option like this:
watch :{
xAxisData: {
handler: function (newData) {
this.option.xAxis.data = newData;
this.chart.clear();
this.chart.setOption(this.option);
},
deep: true,
},
}
It works.
I still have trouble why certain ways of changing data work, while others do not. I tried this example:
watch: {
'$store.state.linedata': function() {this.redraw()}
},
methods: {
redraw() {
this.chartOptions.series[0].data = this.$store.state.linedata
}
},
data() {
return {
chartOptions: {
chart: {
type: this.type
},
series: [{
data: this.$store.state.linedata,
name: "Test Series",
color: this.color
}]
}
}
}
This setup works, whenever I change the linedata in my store, the component gets updated. However, for me it would be more intuitive to update the data like this, without referencing this.chartOptions.series[0].data:
redraw() {
this.$store.state.linedata = [1,2,3,4,5,6]
}
This will update the state correctly, but however not cause to update the component with the new data. Why does the second way not work and is my first way the correct way to do it? I feel like I am missunderstanding some core concept here.What would a best practice look like?
Thank you!
From the Vuex docs you can read:
The only way to actually change state in a Vuex store is by committing a mutation
It means that you should not try to do this.$store.state.linedata = [1,2,3,4,5,6]. It may throw an error depending on your Vuex settings in the console by the way. Instead, create a mutation like so:
mutations: {
updateLineDate(state, lineDate) {
state.lineData = lineData;
}
}
And then call:
this.$store.commit("updateLineDate", [1, 2, 3, 4, 5, 6]);
To automatically update your chart's data, I would suggest creating a computed property in your Vue component. To make it reactive to changes, map your attribute using mapState:
import { mapState } from "vuex";
// ...
computed: {
...mapState("lineData"),
chartData() {
return {
chart: {
type: this.type
},
series: [{
data: this.lineData,
name: "Test Series",
color: this.color
}]
}
}
}
Then remember to provide chartData to your chart component instead of chartOptions in my example.
I am having an issue getting the datalabels plugin to function correctly with my chart in my Angular 5 app.
The chart is displaying as expected with the exception of no labels are being created by the plugin. No console errors are being generated either which seems odd.
my import section looks like:
import {Chart} from 'chart.js';
import 'chartjs-plugin-datalabels';
My chart creation section looks like:
ngAfterViewInit() {
this.canvas = document.getElementById(this.chartID);
this.canvas.height = this.graphHeight;
this.ctx = this.canvas.getContext('2d');
this.myChart = new Chart(this.ctx, {
type: 'horizontalBar',
data: {
labels: this.chartLabels,
datasets: [{
label: 'Percentage',
data: this.chartValues,
borderWidth: 1,
backgroundColor: '#a32d31',
datalabels: {
align: 'end',
anchor: 'start'
}
}]
},
options: {
legend: {
display: false
},
maintainAspectRatio: false,
plugins: {
datalabels: {
color: 'white',
font: {
weight: 'bold'
},
formatter: Math.round
}
}
}
});
}
Is there a separate step needed at some point to register the plugin (the samples provided don't show that). Any ideas or suggestions to get this working? The chart itself looks fine with the exception of the plugin output not being there.
install chartjs-plugin-datalabel by
npm install chartjs-plugin-datalabels --save
Then import the same in component by
import ChartDataLabels from 'chartjs-plugin-datalabels';
and add
labels:[]
..
datasets[]
..
plugin:[ChartDataLabels]
This worked for me . Hope it will work.
I know this isn't exactly the same problem that the OP had, but I had a bit of trouble with the Angular CLI compiling the code properly.
angular.json:
{
"projects": {
"myProject": {
"architect": {
"build": {
"options": {
"scripts": [
"node_modules/chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.js"`
create index.d.ts file with contents:
declare module 'chartjs-plugin-datalabels'
import as follows:
import ChartDataLabels from 'chartjs-plugin-datalabels';
Ok, I'm using Angular V13 with chart.js v 3.7 and chartjs-plugin-datalabels v 2.
I struggled a lot trying to get things working. The problem was not errors, but the labels were just not appearing.
Turned out that I had to add .default to the plugin reference:
public chartPlugins = [pluginDataLabels.default];
Now you can bind this chartPlugins variable to the canvas:
<canvas baseChart
[data]="lineChartData"
[options]="lineChartOptions"
[type]="lineChartType"
[plugins]="chartPlugins"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
And then it works!
Hopefully this helps others:
The error was a result of the axis min value not being defaulted to zero. Once that was applied to the axes all functions as expected.
First of all let me introduce you to my project. I am desining a web application that will show some data about devices scattered around a country. To create this I used Vue.js and HighCharts (HighMaps for the map part). This is the result I have achieved now.
What I want to add now is the possibility for the end-user to click on a marked region and show all of the devices in that region. To do that I need the region's "ID", called code in HighMaps, to send a ajax request to my db and I would also like to make this new "div" a component so that I can use it freely in my application. I'll put a sketch image of what I mean (excuse me for my really bad paint skills :D):
The black lines are not important, what I would like to achieve is to show a new component besides the map (or wherever really). Next is my current code, I am using the one page, one component style so both template and script tags are in the same file and I omitted in the script tag all the unecessary things. Right now I just set up a div with curly brackets to update a variable on change, just to debug more easily. My main problem is that, in the plotOptions.series.point.events.click when I try to reference the this.foo variable it doesn't set it since the div doesn't update. I think that might be a scope issue but I wouldn't know where to start looking.
<template>
<div id="canvasContainer">
<highmaps :options="chartOptions"></highmaps>
<app-componentForDevices><app-componentForDevices>
<div>{{foo}}</div>
</div>
</template>
<script>
import HighCharts from 'vue-highcharts';
import json from '../map.json'
export default {
data: function () {
return {
foo: 'NO',
/* Map */
chartOptions: {
chart: {
map: json, // The map data is taken from the .json file imported above
},
plotOptions: {
series: {
point: {
events: {
click: function () {
this.foo='OK'
}
}
}
},
map: {
joinBy: ['hc-key', 'code'],
allAreas: false,
tooltip: {
headerFormat: '',
pointFormat: '{point.name}: <b>{series.name}</b>'
},
}
},
/* Zoom and move */
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series: [
{
allAreas: true,
showInLegend: false,
},
{
borderColor: '#a0451c',
cursor: 'pointer',
name: 'ERROR',
color: "red",
data: ['it-na', 'it-mi', 'it-mo', 'it-ud'].map(function (code) {
return {code: code};
}),
},
{
borderColor: '#a09e21',
cursor: 'pointer',
name: 'WARNING',
color: "yellow",
data: ['it-ts', 'it-av', 'it-ri'].map(function (code) {
return {code: code};
}),
},
{
borderColor: '#4ea02a',
cursor: 'pointer',
name: "OK",
color: "lightgreen",
data: ['it-pa', 'it-ve', 'it-bo', 'it-ta', 'it-pn'].map(function (code) {
return {code: code};
}),
},
],
}
}
}
}
</script>
<style scoped>
svg{
align-items: center;
}
Thanks in advance for the help. Cheers!
EDIT
I have just tried #icecream_hobbit 's suggestion and using a ECMA6 arrow function helped since now I can access the variable store in Vue but now I lost the access to the local arguments like this.name which made possibile for me to print the selected region. Any ideas? Am I missing something?
EDITv2
Thanks to #icecream_hobbit I have found a way to do what I wanted. You just need to add an object inside the parenthesis so that you can use this for the global variable and e for your mouse click event.
events: {
click: (e) => {
this.foo = 'OK'
this.foo = e.point.name
}
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Until arrow functions, every new function defined its own this value
(a new object in the case of a constructor, undefined in strict mode
function calls, the base object if the function is called as an
"object method", etc.)
The this you were accessing did not belong to the Vue instance. You can use an arrow function () => {/*function body*/} to inherit the this of the Vue instance.
An arrow function expression has a shorter syntax than a function
expression and does not have its own this, arguments, super, or
new.target.
EDIT: For the added question of how do I do I get the Vue instance, and the object instance at the same time you can use a closure.
click: function(VueInstance){
return function() {
VueInstance.Foo = 'OK';
}(this)
}
I use ui-grid in my project with angularjs.
In my project ui-grid exports content to excel file and it's working perfectly.
Here is ui-grid declaration:
and here ui-grid definition in javascript:
$scope.gridOptions = {
columnDefs: [
{ field: 'name' },
{ field: 'company', cellFilter: 'mapCompany:this.grid.appScope.companyCatalog' }
],
enableGridMenu: true,
enableSelectAll: true,
exporterCsvFilename: 'myFile.csv',
exporterPdfDefaultStyle: {fontSize: 9},
exporterPdfTableStyle: {margin: [30, 30, 30, 30]},
exporterPdfTableHeaderStyle: {fontSize: 10, bold: true, italics: true, color: 'red'},
exporterPdfHeader: { text: "My Header", style: 'headerStyle' },
exporterPdfFooter: function ( currentPage, pageCount ) {
return { text: currentPage.toString() + ' of ' + pageCount.toString(), style: 'footerStyle' };
},
exporterPdfCustomFormatter: function ( docDefinition ) {
docDefinition.styles.headerStyle = { fontSize: 22, bold: true };
docDefinition.styles.footerStyle = { fontSize: 10, bold: true };
return docDefinition;
},
exporterPdfOrientation: 'portrait',
exporterPdfPageSize: 'LETTER',
exporterPdfMaxGridWidth: 500,
exporterCsvLinkElement: angular.element(document.querySelectorAll(".custom-csv-link-location")),
data : [
{
"name": "Derek",
"company": 423638
},
{
"name": "Frederik",
"company": 513560
}
],
onRegisterApi: function(gridApi){
$scope.gridApi = gridApi;
},
gridMenuCustomItems: [
{
title:'Custom Export',
action: function ($event) {
// this.grid.api.exporter.csvExport( uiGridExporterConstants.ALL, uiGridExporterConstants.ALL, true );
var exportData = uiGridExporterService.getData(this.grid, uiGridExporterConstants.ALL, uiGridExporterConstants.ALL, true);
var csvContent = uiGridExporterService.formatAsCsv([], exportData, this.grid.options.exporterCsvColumnSeparator);
uiGridExporterService.downloadFile (this.grid.options.exporterCsvFilename, csvContent, this.grid.options.exporterOlderExcelCompatibility);
},
order:0
}
]
};
Here is workin PLUNKER!
But I need to export content to RTL excel file.
My question is how can I export ui-grid content to RTL excel file?
You have two options here. You can use the built-in menu, which I just did successfully on a demo from UIGrid or you can add in another module, the ui.grid.exporter
From the documentation:
This module provides the ability to export data from the grid. Data
can be exported in a range of formats, and all data, visible data, or
selected rows can be exported, with all columns or visible columns. No
UI is provided, the caller should provide their own UI/buttons as
appropriate, or enable the gridMenu
I used the built-in gridMenu, which downloaded a file without an extension called undefined. I was able to open it as is from LibreOffice Calc.
If you want more control, depending on your use case, then use the exporter feature. The exporter feature allows data to be exported from the grid in csv or pdf format. The exporter can export all data, visible data or selected data.
If you want to export as Excel you need to have installed the Excel-Builder module, available through: bower install excelbuilder.
You are leaving out a lot in your question though. The user can set the RTL or LTR options in excel itself, depending on the version. To do it in code will take another library or program of some kind. Years ago I did a lot of MS Word and Excel programming using Visual Basic.
My point is, you need to specify the excel version, do you want to modify the file programmatically, are you sending the file somewhere or does the user download it and then they open it with excel? etc... I need more of the details for your use case.