I cant find a way to implement a live charts with ng2-charts.
I get an error when I do :
<div *ngIf="(lineChartData$ | async)!=null">
....
<canvas baseChart width="100" height="200"
[datasets]="lineChartData$ | async" <<-ERROR: "Cannot read property 'data' of undefined"
....
</canvas>
</div>
I think that even if I would make it work some how, it would be the worst way to do it.
Please recommend on any other liberies that has a build-in live charts if there is no solution to this error.
EDIT:
app.component.html:
<div *ngIf="(lineChartData$ | async)!=null">
<div class="row">
<div class="col-md-6">
<div style="display: block;">
<canvas baseChart width="100" height="200"
[datasets]="lineChartData$ | async" <<<-- ERROR
[labels]="lineChartLabels"
[options]="lineChartOptions"
[colors]="lineChartColors"
[legend]="lineChartLegend"
[chartType]="lineChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
</div>
<div class="col-md-6" style="margin-bottom: 10px">
<table class="table table-responsive table-condensed">
<tr>
<th *ngFor="let label of lineChartLabels"></th>
</tr>
<tr *ngFor="let d of lineChartData">
<td *ngFor="let label of lineChartLabels; let j=index"></td>
</tr>
</table>
</div>
</div>
</div>
app.component.ts:
import {Component, OnInit} from '#angular/core';
import {Observable} from "rxjs";
import {Store} from "#ngrx/store";
import {AppState} from "../../redux/design/app-state";
import {AngularFire, AuthProviders, FirebaseObjectObservable} from "angularfire2";
import 'rxjs/add/operator/withLatestFrom';
import {AuthActions} from "../../redux/actions/auth.actions";
import {UserService} from "../../services/user.service";
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit
{
private lineChartData$:Observable<Array<any>>;
constructor(private authActions: AuthActions,
private af: AngularFire,
private userService:UserService,
private store:Store<AppState>){}
public ngOnInit(): void {
this.lineChartData$=Observable.interval(500)
.map(index=>[{data: [65, 59, index, 81, 56, 55, 40], label: 'Series A'},
{data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B'},
{data: [18, 48, 77, 9, 100, 27, 40], label: 'Series C'}
]);
}
// lineChart
public lineChartLabels:Array<any> = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
public lineChartOptions:any = {
animation: false,
responsive: true,
maintainAspectRatio: false
};
public lineChartColors:Array<any> = [
{ // grey
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)',
pointBackgroundColor: 'rgba(148,159,177,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)'
},
{ // dark grey
backgroundColor: 'rgba(77,83,96,0.2)',
borderColor: 'rgba(77,83,96,1)',
pointBackgroundColor: 'rgba(77,83,96,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(77,83,96,1)'
},
{ // grey
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)',
pointBackgroundColor: 'rgba(148,159,177,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)'
}
];
public lineChartLegend:boolean = true;
public lineChartType:string = 'line';
// events
public chartClicked(e:any):void {
console.log(e);
}
public chartHovered(e:any):void {
console.log(e);
}
}
I don't know much about ng2-charts but the error suggests the problem is somewhere else.
The doc on ngOnInit says:
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.
So this lifecycle event is called after the view is initialized. Your lineChartData$ is defined as:
private lineChartData$:Observable<Array<any>>;
... and it's not initialized until ngOnInit() call. So the view tries to bind lineChartData$ which is still undefined at that time. Thus the error message thrown probably from ng2-charts internals.
Related
I'm stuck to display a list of graph from data of a realtime database firebase.
At the moment I can display a list of graph, however I can't get the data of the list : "listData" .
When I change the lifecycle "ngAfterViewInit" by "ngAfterViewChecked", it works for displaying the Graph and the data of listData, however I want to do this cycle once.
If the lifecycle is ngAfterViewInit I can't display the graph
my script
#ViewChildren('pr_chart') chartElementRefs: QueryList<ElementRef>;
constructor(
public afDB: AngularFireDatabase,
public afSG: AngularFireStorage,
public afAuth: AngularFireAuth,
){ }
listData = []
chartData1 = [];
charts= [];
colorCurve = null;
colorCurveFull = null;
ngAfterViewInit() {
this.chartData1 = [1,2,3,4,5,6];
this.afDB.list('db').snapshotChanges(['child_added']).subscribe(async mybets => {
mybets.forEach(mybet => {
this.listData.push({
data : mybet.payload.exportVal().bankroll)
});
var data = Object.values(mybet.payload.exportVal().bankroll);
({result: this.colorCurve, result_full: this.colorCurveFull} = this.giveColor(Object.values(data)));
});
this.charts = this.chartElementRefs.map((chartElementRef, index) => {
return new Chart(chartElementRef.nativeElement, {
type: 'line',
data: {
labels: Object.keys(this.listData[index]["data"]),
datasets: [
{
label: "Test",
fill: true,
borderCapStyle: 'butt',
borderDash: [],
backgroundColor: this.colorCurve,
borderColor: this.colorCurveFull,
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: 'rgba(75,192,192,1)',
pointBackgroundColor: '#fff',
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: 'rgba(75,192,192,1)',
pointHoverBorderColor: 'rgba(220,220,220,1)',
pointHoverBorderWidth: 2,
pointRadius: 1,
pointHitRadius: 10,
data: Object.values(this.listData[index]["data"]),
spanGaps: false,
}
]
},
options: {
scales: {
yAxes: {
title: {
display: true,
text: "Bankroll (100€/match)",
font: {
size: 10
}
},
ticks: {
precision: 0
}
},
xAxes: {
title: {
display: true,
text: "Number value bets",
font: {
size: 10
}
}
}
},
plugins: {
legend: {
display: false,
}
}
}
});
});
// this.chartData1 = Array.from(Array(this.listData.length).keys())
// console.log("test ", this.chartData1 )
});
}
}
my html script :
<ion-card *ngFor="let data of listData" >
<ion-card-content>
<canvas #pr_chart></canvas> <!--I can't display the graph-->
</ion-card-content>
</ion-card>
<ion-card *ngFor="let data of chartData1" >
<ion-card-content>
{{listData[0].country}} <!--I can't display list data-->
<canvas #pr_chart></canvas> <!--I can display the graph-->
</ion-card-content>
</ion-card>
I've tried to changed by other lifecycle and nothing works correctly... If someone can help me !
I am trying to create 2 distinct line charts which display 2 different datasets. With this, I want to display 2 different colors for each chart.
<div style="display: block" *ngIf='signalRService.data'>
<canvas baseChart id="data1"
[datasets]="signalRService.data1"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="chartType"
[colors]="colors"
(chartClick)="chartClicked($event)"></canvas>
</div>
<div style="display: block" *ngIf='signalRService.data'>
<canvas baseChart id="data2"
[datasets]="signalRService.data2"
[labels]="chartLabels"
[options]="chartOptions"
[legend]="chartLegend"
[chartType]="chartType"
[colors]="colors"
(chartClick)="chartClicked($event)"></canvas>
</div>
At the moment, I have signalRService.dataX which is containing specific data from the larger signalRService.data. When I separate the data into 2 variables to assign them to different charts, the colors and color settings stop working and revert to their defaults (Light gray with a fill). I also have them both on the same componenent.
Fixes I have tried:
I have tried to use the [data] property instead of [datasets] which hasn't worked, as I can't read signalRService.data[0] because it's undefined.
I tried to create 2 separate arrays for colors, which hasn't worked either.
I have looked into, but haven't really figured out how I can use [datasets]="signalRService.data" and then hide a different dataset for each chart.
The best solution would be if I was able to adjust colors and maintain the rest of the code or to be able to hide a dataset on each graph (I will have 3, so I'd want to hide 2 of 3 datasets). However, I'm open to any ideas, even what I might have "tried" but done incorrectly.
Below is my app.component.ts file in case it's necessary.
import { Component, OnInit } from '#angular/core';
import { SignalRService } from './services/signal-r.service';
import { HttpClient } from '#angular/common/http';
import { ChartOptions } from 'chart.js';
import { Label } from 'ng2-charts';
import * as pluginCrosshair from 'chartjs-plugin-crosshair';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
public chartOptions: (ChartOptions) = {
responsive: true,
spanGaps: false,
animation: {
duration: 0
},
scales: {
// We use this empty structure as a placeholder for dynamic theming.
xAxes: [{}],
yAxes: [
{
id: 'y-axis-0',
position: 'left',
ticks: {
max : 30,
min: -30
}
},
]
},
plugins: {
tooltips: {
mode: "interpolate",
intersect: false,
},
crosshair: {
line: {
color: '#F66', // crosshair line color
width: 2, // crosshair line width
value: '0'
},
sync: {
enabled: false, // enable trace line syncing with other charts
//group: 1, // chart group
suppressTooltips: false // suppress tooltips when showing a synced tracer
},
},
zoom: {
enabled: false,
},
}
};
public chartType: string = 'line';
public chartLegend: boolean = true;
public chartLabels: Label[] = ['0', '1', '2', '3', '4', '5','6','7','8','9','10','11','12','13','14','15','16','17',
'18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40',
'41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59'];
public chartPlugins = [pluginCrosshair];
public colors: any[] = [{ // grey
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)',
pointBackgroundColor: 'rgba(148,159,177,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)',
fill: false
},
{
backgroundColor: 'rgba(0,100,0,0.2)',
borderColor: 'rgba(0,0,100,1)',
pointBackgroundColor: 'rgba(148,159,177,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)',
fill: false
}];
constructor(public signalRService: SignalRService, private http: HttpClient) { }
ngOnInit() {
this.signalRService.startConnection();
this.signalRService.addTransferChartDataListener();
this.signalRService.addBroadcastChartDataListener();
this.startHttpRequest();
}
private startHttpRequest = () => {
this.http.get('https://localhost:5001/api/chart')
.subscribe(res => {
console.log(res);
})
}
public chartClicked = (event) => {
console.log(event);
this.signalRService.broadcastChartData();
}
}
I actually figured this out. If I replace the reference in the html to the colors array with just the information itself like this
[colors]="
[{
backgroundColor: 'rgba(0,100,0,0.2)',
borderColor: 'rgba(0,0,100,1)',
pointBackgroundColor: 'rgba(148,159,177,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(148,159,177,0.8)',
fill: false
}]"
and I also change the data1 and data2 variables to slices (using Arrays.slice) of the original super dataset, it works.
I want to create a chart with the chart bar and list all the new records that I added in my database and get the month it was added.
The problem is my application is in php and I saw that chart.js is in javascript.
This is the html of my chart that is in index.php
<div class="row clearfix">
<!-- Bar Chart -->
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="header">
<h2>GuiaCorretor - Analitycs</h2>
</div>
<div class="body">
<canvas id="bar_chart" height="50"></canvas>
</div>
</div>
</div>
<!-- #END# Bar Chart -->
</div>
And this is the code that is in my javascript, but here there is no link to my database, how could I sync my javascript and get this information?
$(function () {
//new Chart(document.getElementById("line_chart").getContext("2d"), getChartJs('line'));
new Chart(document.getElementById("bar_chart").getContext("2d"), getChartJs('bar'));
//new Chart(document.getElementById("radar_chart").getContext("2d"), getChartJs('radar'));
//new Chart(document.getElementById("pie_chart").getContext("2d"), getChartJs('pie'));
});
function getChartJs(type) {
var config = null;
if (type === 'bar') {
config = {
type: 'bar',
data: {
labels: ["JANEIRO", "FEVEREIRO", "MARÇO", "ABRIL", "MAIO", "JUNHO", "JULHO", "AGOSTO", "SETEMBRO", "OUTUBRO", "NOVEMBRO", "DEZEMBRO"],
datasets: [{
label: "Imóveis cadastrados",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: 'rgba(0, 188, 212, 0.8)'
}, {
label: "Imóveis cadastrados",
data: [28, 48, 40, 19, 86, 27, 90],
backgroundColor: 'rgba(233, 30, 99, 0.8)'
}]
},
options: {
responsive: true,
legend: false
}
}
}
return config;
}
One way is to use your controller to create the data in an array format, and pass that data into your view as a json string.
In php create the data array that .js needs:
$chartData = [
'labels' => ['Janeiro', 'Fevereiro', ...],
'datasets' => [
[
'label' => "Imóveis cadastrados",
'data' => [65, 59, 80, 81, 56, 55, 40],
'backgroundColor' => 'rgba(0, 188, 212, 0.8)'
],
[...]
]
];
Then json_encode that data as a data attribute of your canvas (the syntax here will vary depending on what method you use to pass dynamic data into your view, I will use raw PHP here for demonstration):
<canvas id="bar_chart" height="50" data-chart-data="<?php json_encode($chartData); ?>"></canvas>
And then lastly use the json contained in that data attribute to power your chart:
config = {
type: 'bar',
data: document.getElementById("bar_chart").data('chart-data'),
options: {
responsive: true,
legend: false
}
}
Trying to reuse a custom template from my Graph.vue file, but this attempt fails(without any errors in the console). I only get one chart rendered(the red one). Any ideas how to fix this code?
My current code looks like this:
main.js
import Vue from 'vue';
import Graph from './components/Graph.vue';
new Vue({
el: 'graph',
components: { Graph }
});
Graph.vue
<template>
<canvas height="400" width="600"></canvas>
</template>
<script>
import Chart from 'chart.js';
export default {
props: ['labels', 'values', 'color'],
props: {
labels: {},
values: {},
color: {
default: 'rgba(220,220,220,0.2)'
}
},
mounted(){
var data = {
labels: this.labels,
datasets: [
{
label: "My First dataset",
fill: true,
lineTension: 0.1,
backgroundColor: this.color,
borderColor: "rgba(75,192,192,1)",
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: "rgba(75,192,192,1)",
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgba(75,192,192,1)",
pointHoverBorderColor: "rgba(220,220,220,1)",
pointHoverBorderWidth: 2,
pointRadius: 1,
pointHitRadius: 10,
data: this.values,
spanGaps: false
},
]
};
new Chart(this.$el, {type: 'line', data: data});
}
}
</script>
example.html
<div style="width:600px" class="container">
<graph :labels="['January', 'February', 'March']"
:values="[10, 42, 4]"
color="red"
></graph>
</div>
<div style="width:600px" class="container">
<graph :labels="['May', 'June', 'July']"
:values="[100, 420, 99]"
color="blue"
></graph>
</div>
<script src="{{asset('/js/main.js')}}"></script>
The intended result should be two bars - red and blue one.
I think your mountpoint is wrong. el: 'graph' behavior is probably not predictable in this context (will it target the first graph element?).
Use something like that instead:
JS:
new Vue({
el: '#graphContainer',
components: { Graph }
});
HTML:
<div id="graphContainer">
<div style="width:600px" class="container>
<graph :labels="['January', 'February', 'March']"
:values="[10, 42, 4]"
color="red"></graph>
</div>
<div style="width:600px" class="container">
<graph :labels="['May', 'June', 'July']"
:values="[100, 420, 99]"
color="blue"></graph>
</div>
</div>
I like #Cobaltway answer better, but this also solves the problem.
JS:
import Vue from 'vue';
import Graph from './components/Graph.vue';
const graphs = document.querySelectorAll('graph');
for (let i = 0; i < graphs.length; ++i) {
new Vue({
el: graphs[i],
components: { Graph }
});
}
I have started to use angular2 ng2-chart. I have few questions regarding the below image which I created using angular2 ng2-chart but still want to do more customization:
Questions:
1) How can I draw a dotted-line between two points when there are no values like in above image Nov-7 has value 0 (zero)?
2) How can I make a shadow effect, opacity or a combination of more than one colors?
3) How can I get the value of y-axis when I hover on any of the defined point and also if I want to change the y-axis grid color on mouse hover. What is the best way to do it using ng2-chart hover function?
Current sample code and config file:
index.html
<div class="container">
<div class="row">
<div class="overview-page">
<div class="overview-page-title">
<h2>Overview</h2>
</div>
<div class="chart-view">
<canvas baseChart
class="chart"
[datasets]="charts.datasets"
[labels]="charts.labels"
[colors]="charts.chartColors"
[options]="charts.options"
[legend]="false"
[chartType]="charts.type"
(chartHover)="chartHovered($event)">
</canvas>
</div>
</div>
</div>
</div>
index.component.ts
import {Component, Output, EventEmitter, OnInit} from '#angular/core';
import {Router} from '#angular/router';
import {Config} from '../../../config/config';
#Component({
templateUrl: 'index.html',
styleUrls: ['../../../../common/stylesheets/pages/index.scss']
})
export class IndexComponent implements OnInit {
protected charts: any;
ngOnInit() {
this.charts = (<any>Config.get('test')).charts;
console.log(this.charts);
}
chartHovered(e:any):void {
console.log(e);
}
}
Config.ts:
import * as Immutable from 'immutable';
export const Config = Immutable.Map({
test: {
charts: {
datasets: [{
data: [40, 48.2, 0, 52.6, 51.1, 57.6, 74.8]
}],
labels: ['Nov 5', 'Nov 6', 'Nov 7', 'Nov 8', 'Nov 9', 'Nov 10', 'Nov 11'],
type: 'line',
options: {
scales: {
xAxes: [{
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 1
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
max: 100,
min: 0,
stepSize: 25
},
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 0.5
}
}]
},
responsive: true
},
chartColors: [{
backgroundColor: 'rgba(25,10,24,0.2)',
borderColor: 'rgba(225,10,24,0.2)',
pointBackgroundColor: 'rgba(225,10,24,0.2)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(225,10,24,0.2)'
}]
}
}
});
I couldn't find the best answer to your first question. However you can define multiple datasets with no intersection and use different colours (see the point 2) for that one.
http://valor-software.com/ng2-charts/
For the second one when you define colours, as you are already doing it in your code:
chartColors: [{
backgroundColor: 'rgba(25,10,24,0.2)',
borderColor: 'rgba(225,10,24,0.2)',
pointBackgroundColor: 'rgba(225,10,24,0.2)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(225,10,24,0.2)'
}
The last number in rgba is the opacity. For having different colours the option is to define multiple datasets, otherwise it randomises the colours and you won't get mixed ones. A plunker here:
http://plnkr.co/edit/9PckMZiDYZjRz1PA0Suq
For the last question regarding getting the value of x-axis, look at the event which is logged to console on bounded events.