Implement ng2-charts in an Angular-Seed - chart.js

I was unable to to compile a Production AOT build ("npm run build.prod.rollup.aot") in Angular Seed - after implementing ng2-charts in my app (when I run "npm run start.deving - I could've seen the chart, but it would fail for Karma UnitTesting - in the ***.spec.ts file).

The following solution did the work for me (after installing ng2-chart & charts.js via npm):
import ChartsModule in your main module (e.g. "home.module.ts"):
import { ChartsModule } from 'ng2-charts/ng2-charts';
Then - provide it to let other components (that are inside this module) use this module (do the same in your equivalent "...spec.ts" file (e.g. "home.component.spec.ts"):
#NgModule({
imports:[ChartsModule,...]
)}
Fix Karma UnitTesting & AOT compilation by amending the following to your project.config.ts
--
this.ROLLUP_NAMED_EXPORTS = [
...this.ROLLUP_NAMED_EXPORTS,
{'node_modules/ng2-charts/ng2-charts.js': ['ChartsModule']}
]
let additionalPackages: ExtendPackages[] = [
// required for dev build
{
name: 'ng2-charts/ng2-charts',
// Path to the package's bundle
path: 'node_modules/ng2-charts/bundles/ng2-charts.umd.min.js'
},
Just to make sure, I've added the actual chart implementation component (e.g. "line-chart.component.ts"):
import { Component } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'line-chart',
templateUrl: './line-chart.component.html',
styleUrls: ['line-chart.component.css'],
})
export class LineChartComponent {
// lineChart
public lineChartData:Array<any> = [
{data: [28, 48, 40, 19, 110, 27, 120, 28, 48, 40, 19, 110, 27, 60, 120, 28, 48, 40, 19, 110, 27, 120, 28, 48, 40, 19, 110, 27, 60, 120], label: ''}
];
public lineChartLabels:Array<any> = ['', '', '', '', '', '', '','', '', '', '', '', '', '','', '', '', '', '', '', '','', '', '', '', '', '', '', '', ''];
public lineChartOptions:any = {
scales: { // remove grid lines
xAxes: [{
display: false,
gridLines: {
display:false
}
}],
yAxes: [{
display: false,
gridLines: {
display:false
}
}]
},
responsive: true,
legend: {display:false}
};
public lineChartColors:Array<any> = [
{
backgroundColor: 'transparent',
borderColor: '#9C0100',
pointBackgroundColor: '#7E0001',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)'
}
];
public lineChartLegend:boolean = true;
public lineChartType:string = 'line';
public randomize():void {
let _lineChartData:Array<any> = new Array(this.lineChartData.length);
for (let i = 0; i < this.lineChartData.length; i++) {
_lineChartData[i] = {data: new Array(this.lineChartData[i].data.length), label: this.lineChartData[i].label};
for (let j = 0; j < this.lineChartData[i].data.length; j++) {
_lineChartData[i].data[j] = Math.floor((Math.random() * 100) + 1);
}
}
this.lineChartData = _lineChartData;
}
// events
public chartClicked(e:any):void {
console.log(e);
}
public chartHovered(e:any):void {
console.log(e);
}
}
and use it in each component (that is part of the module):
<line-chart></line-chart>

I'm not using ng2-charts in my Angular 6.1 website; however, I am using chart.js by itself in mgechev/angular-seed. I decided I don't need or want the extra ng2-charts wrapper. In the end, you're really just using chart.js anyway.
As of writing this response, Chart.js developers have some work to do to make exporting its members compliant with newer standards.
To get Chart.js to work, all you need to do is:
Update project.config.ts to include ROLLUP_NAMED_EXPORTS
this.ROLLUP_NAMED_EXPORTS = [
...this.ROLLUP_NAMED_EXPORTS,
{ 'node_modules/chart.js/src/chart.js': ['Chart'] }
];
Update project.config.ts to include additional packages
// Add packages
const additionalPackages: ExtendPackages[] = [
{ name: 'chart.js', path: 'node_modules/chart.js/dist/Chart.bundle.min.js' },
...
];
In your component
import { Chart } from 'chart.js';
...
export class MyComponent implements OnInit {
#ViewChild('myChart') myChartRef: ElementRef;
chartObj: Chart;
...
}
Then load chart configuration in ngOnInit() per Chart.js documentation
HTML will look something like this:
<div class="chart-container">
<canvas #myChart></canvas>
</div>

Related

Charts.js renders axes, but not the dataset

I'm trying to use Charts.js on an AWS Lambda function to create a chart image (png).
However, for some reason it plots the axes, but no data.
this is my code:
export const plotData = (values: number[]): Buffer | null => {
const canvas = createCanvas(800, 600);
let ctx: ChartItem = canvas as unknown as ChartItem;
const plugin: Plugin = {
id: "customCanvasBackgroundColor",
beforeDraw: (chart: any, _args: any, options: any) => {
const { ctx: context } = chart;
context.save();
context.globalCompositeOperation = "destination-over";
context.fillStyle = options.color || "#99ffff";
context.fillRect(0, 0, chart.width, chart.height);
context.restore();
},
};
const chart = new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "ph",
data: values.map((y) => ({
y,
t: new Date(),
})),
borderWidth: 2,
borderColor: "red",
backgroundColor: "rgb(255, 0, 0, 0.5)",
},
],
},
options: {
responsive: false,
animation: false,
scales: {
y: {
beginAtZero: true,
},
},
plugins: {
legend: {
position: "top",
},
title: {
display: true,
text: "TEstuibg",
},
customCanvasBackgroundColor: {
color: "rgba(255,255,255, 1)",
},
},
},
plugins: [plugin],
});
// chart.draw();
chart.update();
return canvas.toBuffer("image/png");
};
And this is what it is rendering when I call plotData([100, 200, 300, 400, 500, 1600]):
I am already disabling animations and responsiveness. Is there something else I need to do?
I would create the image on the onComplete event, than everything should be visible, atleast this works in browsers.
from the documentation: "...The animation configuration provides callbacks which are useful for synchronizing an external draw to the chart animation....", but works surely for your image creating process. link to documentation
...
options: {
animation:{
duration: 0, // "no" animation
onComplete: () => {
...
// create image
...
}
}
}
...
Ofcourse: in this case your function plotData would have to be async or pass a callback function for when the event onComplete fires.

getting error "this.renderChart is not a function" or "TypeError: Object(...) is not a function" Chart.js Nuxt v2

i want to use chart.js in my nuxt project which is version ^2.15.7.
The issue is that I having these errors in my console when trying to use this plugin in my .vue page.
errors:
this.renderChart is not a function
TypeError: Object(...) is not a function
here is my codes:
nuxt.config.js
plugins: [
{src: '~/plugins/chart.js', mode: 'client'},
],
/plugins/chart.js
import Vue from 'vue'
import { Line } from 'vue-chartjs'
Vue.component('line-chart', {
extends: Line,
props: ['data', 'options'],
mounted () {
this.renderChart(this.data, this.options)
}
})
.vue page
<template>
<client-only>
<line-chart :data="chartData"></line-chart>
</client-only>
</template>
<script>
export default {
data() {
return {
chartData: {
datasets: [{
label: 'Title',
data: [45, 55, 48, 35, 12]
}]
}
};
}
}
</script>
after many searches i finally found out nuxt v2 cant import and use "vue-chartjs" and instead of "vue-chartjs" we should use the "vue-chartjs/legacy",
here is the solution:
installation
1-Run
npm i vue-chartjs
2-Then Run
npm i chart.js hchs-vue-charts
3-/plugins/chart.js
import Vue from "vue";
import { Line } from "vue-chartjs/legacy";
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
LineElement,
PointElement,
} from "chart.js";
ChartJS.register(
Title,
Tooltip,
Legend,
PointElement,
BarElement,
CategoryScale,
LinearScale,
LineElement,
);
Vue.component("line-chart", {
extends: Line,
});
4-.vue page
<template>
<client-only placeholder="منتظر بمانید...">
<line-chart
:chart-options="options"
:chart-data="chartData"
:height="250"
:width="350"
chart-id="lineChart"
/>
</client-only>
</template>
<script>
chartData: {
labels: ['sun','mon','tues'],
datasets: [
{
label: 'Views',
backgroundColor: ["tomato", "orange", "yellow"],
data: ['0','2','5']
}
]
},
options:{
responsive: true,
legend: {
display: false,
},
title: {
display: true,
text: 'views'
},
scales: {
y: {
suggestedMin: 0,
ticks: {
precision: 0
}
}
}
},
</script>
5-nuxt.config.js (don't forget the mode:'client')
plugins: [
{src: '~/plugins/chart.js', mode: 'client'},
],

How to select context or scale on chart in chartjs quickchart io to run getValueForPixel?

I am trying to run a function to create gradient line on my chart like:
y.getValueForPixel(50),
and for that I need chart pixel for a certain value, but I can't figure out to select it on quickchart, as context is undefined there.
On the front end I am doing it like this:
borderColor: function(context, options) { // context is automatically fetched here from chart
let chart = context.chart;
let {
ctx,
chartArea
} = chart;
if (!chartArea) {
return null;
}
return getGradient(ctx, chartArea, chart);
}
getGradient function (for frontend):
function getGradient(ctx, chartArea, chart) {
let chartWidth = chartArea.right - chartArea.left;
let chartHeight = chartArea.bottom - chartArea.top;
if (gradient === null || width !== chartWidth || height !== chartHeight) {
width = chartWidth;
height = chartHeight;
let {
scales: {
x,
y
}
} = chart;
gradient = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
gradient.addColorStop(0, 'green');
gradient.addColorStop(y.getPixelForValue(50) / y.getPixelForValue(100), 'yellow')
}
return gradient;
}`;
But I can't figure out how to select context on quickchart io, any help would be great.
The full chart context is unavailable in the Community version of QuickChart at this time for security reasons. You'll either have to upgrade to Professional or self-host the service.
However, there is a built-in function getGradientFillHelper that wraps some of this access and may achieve the desired effect.
Here's an exmaple:
{
type: 'line',
data: {
labels: [2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020],
datasets: [
{
data: [12, 6, 5, 18, 12, 20, 50, 80, 90],
fill: false,
borderColor: getGradientFillHelper('vertical', [
'#eb3639',
'#a336eb',
'#36a2eb',
]),
borderWidth: 5,
pointRadius: 0,
},
],
},
options: {
legend: {
display: false,
},
},
}
Link to this chart in Quickchart editor
getGradientFillHelper documentation

ChartJS line-chart set background-opacity

I've tried multiple examples but i'm unable to get my line-chart to have background-transparency, as of now, it's blue, but the color i'm trying to set is orange (rgba(255, 145, 68, 0.2)).
TL;DR: How do I set the fill-opacity on my line-chart graph?
I've followed countless of tutorials, but all of them seem to set the background-color in dataset, which is not what I want, since i'm using dynamic data. The code is in a TypeScript component with tons of functionality built upon it, so it's quite hard to extract the code and make it work in a jsfiddle.
When i set fill:true under elements: { line: { the graph will get the fill as shown in the image below. But i'm unable to set the background-opacity here,
I've tried these attributes: backgroundColor, fillColor
Here's the code atleast:
import {
Component
} from '../../decorators/component.decorator';
import * as EsChart from '../../../../node_modules/chart.js/dist/Chart.bundle.js';
import * as angular from 'angular';
#Component({
templateUrl: 'app/dashboard-components/es-line-chart/es-line-chart.component.html',
bindings: {
esLineChartId: '#',
chartTitle: '#',
labels: '#',
esScaleMax: '#',
esScaleMaxSecond: '#',
esScaleStepW: '#',
esScaleStepWSecond: '#',
esScaleMin: '#',
esScaleMinSecond: '#',
lowerOkValue: '#',
targetValue: '#',
upperOkValue: '#',
chartDataValueSuffix: '#',
datasets: '#',
esXLabelSkips: '#'
}
})
export default class LineChartComponent {
$ctrl: any = this;
getChartOptions(lineChartData, secondYAxis, xLabelSkips) {
return {
type: 'line',
data: lineChartData,
options: {
responsive: true,
maintainAspectRatio: false,
title: {
display: true,
text: this.$ctrl.chartTitle
},
legend: {
display: true,
labels: {
boxWidth: 12
}
},
tooltips: {
enabled: true,
mode: 'index',
intersect: false,
filter: function(tooltipItem, data) {
return !data.datasets[tooltipItem.datasetIndex].tooltipHidden;
},
callbacks: {
label: function(tooltipItem, data) {
var currentDataset = data.datasets[tooltipItem.datasetIndex];
var label = currentDataset.label + ': ';
label += currentDataset.data[tooltipItem.index] + (currentDataset.dataSuffix ? " " + currentDataset.dataSuffix : "");
return label;
}
}
},
elements: {
line: {
tension: 0, // disables bezier curves
borderWidth: 3, // Line stroke width in pixels
fill: true,
fillColor: "rgba(255, 145, 68, 0.2)",
strokeColor: "rgba(255, 145, 68, 0.2)",
pointColor: "rgba(255, 145, 68, 1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(255, 145, 68, 1)"
},
point: { // disable the circles/points in the chart lines
radius: 0,
hitRadius: 0,
hoverRadius: 0
},
},
scales: {
xAxes: [{
display: true,
type: 'category',
ticks: {
autoSkip: xLabelSkips,
maxRotation: 45
},
afterFit: (scale) => {
scale.height = 100;
}
}],
yAxes: [{
id: 'y-axis-0',
display: true,
position: 'left',
ticks: {
stepSize: this.$ctrl.esScaleStepW ? eval(this.$ctrl.esScaleStepW) : 0.5, // grid lines on yAxis with value distance of stepSize
suggestedMin: (eval(this.$ctrl.esScaleMin)),
suggestedMax: (eval(this.$ctrl.esScaleMax))
}
},
{
id: 'y-axis-1',
display: secondYAxis,
position: 'right',
ticks: {
stepSize: this.$ctrl.esScaleStepWSecond ? eval(this.$ctrl.esScaleStepWSecond) : 0.5, // grid lines on yAxis with value distance of stepSize
suggestedMin: (eval(this.$ctrl.esScaleMinSecond)),
suggestedMax: (eval(this.$ctrl.esScaleMaxSecond))
}
}
]
}
}
};
}
getExtraDatasets(datasetType: string, dataLabel: string, fillValue: number, chartSpan: number) {
var dataFill = [];
for (var i = 0; i < chartSpan; i++) {
dataFill.push(fillValue);
}
return {
label: dataLabel,
borderColor: datasetType === "limit" ? 'rgba(205, 8, 4, 1)' : datasetType === "target" ? 'rgba(20, 180, 2, 1)' : 'rgba(0, 0, 0, 0.5)',
borderWidth: 0.5,
//tooltipHidden: true,
data: dataFill
};
}
removeEmptyDatasets() {
//var test = "[{label: 'Verklig cykeltid',borderColor: 'rgba(60, 141, 188, 0.75)',borderWidth: 4,data: },{label: 'Kalkylerad cykeltid',borderColor: 'rgba(205, 108, 104, 0.75)',borderWidth: 4,data: [59]}]";
this.$ctrl.datasets = this.$ctrl.datasets.replace(/data: *,/g, "data: [],");
this.$ctrl.datasets = this.$ctrl.datasets.replace(/data: *}/g, "data: []}");
this.$ctrl.datasets = this.$ctrl.datasets.replace(/: *,/g, ": 0,");
this.$ctrl.datasets = this.$ctrl.datasets.replace(/: *}/g, ": 0}");
var sets = this.$ctrl.datasets.match(/\{[^\{\}]*\}/g);
for (var i = 0; i < sets.length; i++) {
if (sets[i].match(/data: *\[\]/g)) {
//console.log("no data", sets[i]);
sets[i] = "";
}
}
this.$ctrl.datasets = ("[" + (sets.join()).replace(/^,|,$/g, "") + "]");
}
$onInit() {
var canvas = $("#" + this.$ctrl.esLineChartId + " .lineChart").get(0).getContext("2d");
this.removeEmptyDatasets();
//console.log(this.$ctrl.datasets);
var lineChartData = {
labels: this.$ctrl.labels ? eval(this.$ctrl.labels) : [],
datasets: this.$ctrl.datasets ? eval(this.$ctrl.datasets) : []
};
if (this.$ctrl.upperOkValue) {
lineChartData.datasets = lineChartData.datasets.concat(this.getExtraDatasets("limit", "Övre gräns", eval(this.$ctrl.upperOkValue), (lineChartData.labels).length));
}
if (this.$ctrl.targetValue) {
lineChartData.datasets = lineChartData.datasets.concat(this.getExtraDatasets("target", "Målvärde", eval(this.$ctrl.targetValue), (lineChartData.labels).length));
}
if (this.$ctrl.lowerOkValue) {
lineChartData.datasets = lineChartData.datasets.concat(this.getExtraDatasets("limit", "Undre gräns", eval(this.$ctrl.lowerOkValue), (lineChartData.labels).length));
}
var secondYAxis = false;
for (var i = 0; i < (lineChartData.datasets).length; i++) {
// set backgroundColor to be the same as borderColor so that tooltip and legend items look better
(lineChartData.datasets[i]).backgroundColor = (lineChartData.datasets[i]).borderColor;
// Activate the second y axis if a dataset uses it
if ((lineChartData.datasets[i]).yAxisID == 'y-axis-1') {
secondYAxis = true;
}
// Use standard data suffix if defined and no special data suffix is defined for the dataset
if (!((lineChartData.datasets[i]).dataSuffix) && (this.$ctrl.chartDataValueSuffix)) {
(lineChartData.datasets[i]).dataSuffix = this.$ctrl.chartDataValueSuffix;
}
}
var xLabelSkips = this.$ctrl.esXLabelSkips ? eval(this.$ctrl.esXLabelSkips) : false;
var chartOptions = this.getChartOptions(lineChartData, secondYAxis, xLabelSkips);
var lineChart = new EsChart(canvas, chartOptions);
}
}
resources i've followed:
http://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/
https://www.chartjs.org/docs/latest/configuration/elements.html
https://canvasjs.com/docs/charts/chart-options/data/fill-opacity/
Image:

Chart.js - How to set a line chart dataset as disabled on load

Using chart.js v2, is it possible to mark a dataset in a line chart as being disabled on initial load?
Didn't find an option for it in the documentation.
Yes, there is a "hidden" flag in ChartJS.
eg.
data:
{
datasets: [
{
data: [1,2,3],
label: 'My First Dataset',
hidden: true,
},
],
}
See this issue on GitHub: https://github.com/chartjs/Chart.js/issues/689
The accepted solution has the downside, that hiding/unhiding signals might sometimes fail after initializing the chart like that.
It might be a better idea to change it in the current metadata of the dataSet, which holds the actual data that is used by the chart:
chart.data.datasets.forEach((dataSet, i) => {
var meta = chart.getDatasetMeta(i);
meta.hidden = (<your-condition-here>);
});
this.chart.update();
If you are using angular-chartjs, then you can add the properties of the dataset in the chart-dataset-override property:
For example:
HTML:
<div class="container" ng-app="app" ng-controller="ChartCtrl">
<canvas id="bar" class="chart chart-bar" chart-data="data" chart-labels="labels" chart-series="series" chart-dataset-override="datasetOverride">
</canvas>
</div>
Javascript:
Chart.defaults.global.legend.display = true;
angular.module("app", ["chart.js"])
.controller("ChartCtrl", function($scope) {
$scope.labels = ['2006', '2007', '2008', '2009', '2010', '2011', '2012'];
$scope.series = ['Series A', 'Series B'];
$scope.data = [
[65, 59, 80, 81, 56, 55, 40],
[28, 48, 40, 19, 86, 27, 90]
];
$scope.datasetOverride = [{}, {
hidden: true,
}];
});