amcharts3-angular2
amcharts3-angular2 copied to clipboard
Charts are not shown on the page
Hello,
I have attempted to use Amcharts in one of my Angular 2 projects. But I am unable to succeed. It does not show any error, but the charts are not displayed. Can somebody find where the mistake is?
Thanks in advance!
amchartdemo.component.ts
`import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
@Component({ selector: 'app-amchartdemo', templateUrl: './amchartdemo.component.html', styleUrls: ['./amchartdemo.component.css'] }) export class AmchartdemoComponent implements OnInit { private id: string = "chartdiv";
private data: any = [{ country: "USA", visits: 3025, color: "#FF0F00" }, { country: "China", visits: 1882, color: "#FF6600" }, { country: "Japan", visits: 1809, color: "#FF9E01" }, { country: "Germany", visits: 1322, color: "#FCD202" }, { country: "UK", visits: 1122, color: "#F8FF01" }, { country: "France", visits: 1114, color: "#B0DE09" }, { country: "India", visits: 984, color: "#04D215" }, { country: "Spain", visits: 711, color: "#0D8ECF" }, { country: "Netherlands", visits: 665, color: "#0D52D1" }, { country: "Russia", visits: 580, color: "#2A0CD0" }, { country: "South Korea", visits: 443, color: "#8A0CCF" }, { country: "Canada", visits: 441, color: "#CD0D74" }];
private chart: any = makeChart({ dataProvider: this.data, fillColors: "red" });
change() { this.chart = makeChart({ dataProvider: this.data.map((x: Data) => { return { country: x.country, visits: Math.floor(Math.random() * 100), color: x.color }; }), fillColors: "green" }); }
ngOnInit() { }
}
interface Data { country: string; visits: number; color: string; }
interface Configuration { dataProvider: Array<Data>; fillColors: string; }
const makeChart = ({ dataProvider, fillColors } : Configuration) => { return { "type": "serial", "theme": "light", "marginRight": 70, "dataProvider": dataProvider, "valueAxes": [{ "axisAlpha": 0, "position": "left", "title": "Visitors from country" }], "startDuration": 1, "graphs": [{ "balloonText": "[[category]]: [[value]]", "fillColorsField": "color", "fillAlphas": 0.9, "lineAlpha": 0.2, "type": "column", "valueField": "visits", "fillColors": fillColors }], "chartCursor": { "categoryBalloonEnabled": false, "cursorAlpha": 0, "zoomable": false }, "categoryField": "country", "categoryAxis": { "gridPosition": "start", "labelRotation": 45 }, "export": { "enabled": true } }; }; ` amchartdemo.component.html
`
amchartdemo
<button (click)="change()"> Change data + colors <amCharts [id]="id" [options]="chart" [style.width.%]="100" [style.height.%]="100"></amCharts> `
app.module.ts
`import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { AmChartsDirective } from "amcharts3-angular2/amcharts.directive";
import { AmchartdemoComponent } from './amchartdemo/amchartdemo.component';
@NgModule({ declarations: [ AppComponent, AmchartdemoComponent, AmChartsDirective, ], imports: [ BrowserModule, FormsModule, HttpModule, ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } `
index.html
`<!doctype html>
I have same issue after following instructions.
You are setting the chart id dynamically. Maybe the makeChart
is calling before the template is done (Angular lifecycle).
Also, update your code following the new instructions, use the chart as a private attribute of your component and inject AmCharts on the constructor.
Option 1: Run makeChart
inside of ngOnInit()
import { AfterViewInit } from '@angular/core';
//...
export class SomeComponent implements OnInit {
//...
private chart: any;
private id: string = "chartdiv";
constructor(private AmCharts: AmChartsService) {
}
//...
ngOnInit(): void {
// ...
this.chart = this.AmCharts.makeChart(this.id, {
// ...
}
// ...
}
//...
}
Option 2: Run the makeChart
inside of ngAfterViewInit
https://angular.io/docs/ts/latest/api/core/index/AfterViewInit-class.html
import { AfterViewInit } from '@angular/core';
//...
export class SomeComponent implements AfterViewInit {
//...
private chart: any;
private id: string = "chartdiv";
constructor(private AmCharts: AmChartsService) {
}
//...
ngAfterViewInit(): void {
// ...
this.chart = this.AmCharts.makeChart(this.id, {
// ...
}
// ...
}
//...
}
i have same issue
you must change <amCharts [id]="id" [options]="chart" [style.width.%]="100" [style.height.%]="100"> to <amCharts [id]="{{id}}" [options]="chart" [style.width.%]="100" [style.height.%]="100">
just try this
import { AmChart } from '@amcharts/amcharts3-angular';
import { AmChartsModule } from '@amcharts/amcharts3-angular/amcharts.directive';
ngAfterViewInit() {
setTimeout(()=>{
AmCharts.makeChart('chartdiv', {
}
},1000);
}
Hi, i'm have the same issue, but what i wanna do is create a lot of graphs, i'm trying this by this:
<div class="container" *ngFor="let item of dataDB; let i=index"><h1 id="canvas{{i}}">{{item}}</h1>
<div [id]="chartNumber" [style.width.%]="100" [style.height.px]="500"></div>
but all i get its empty divs
Hello,
I'm facing the same problem. The solution of @aslampeerbits, work well :
import { AmChart } from '@amcharts/amcharts3-angular';
import { AmChartsModule } from '@amcharts/amcharts3-angular/amcharts.directive';
ngAfterViewInit() {
setTimeout(()=>{
AmCharts.makeChart('chartdiv', {
}
},1000);
}
In fact, it's not a proper way to solve it.
I also tried to create the chart in a @ViewChild settter like this :
private _plannedOFChartEL: ElementRef;
@ViewChild('YEAH')
set plannedOFChartEL(el: ElementRef){
this._plannedOFChartEL = el;
if (el) {
// GRAPH CREATION THERE !
}
}
get plannedOFChartEL(): ElementRef{
return this._plannedOFChartEL;
}
But it doesn't work.
Do you have another way to solve it ?
Having the same problem. None of the solutions here work for me.
@calloncampbell Hi,
Inside the new material tabs for angular the chart does not draw after the view init.
@calloncampbell to be more specific adding:
this.chart = this.AmCharts.makeChart('chartdiv', this.getAmchartOptions(dataMap), 1);
where the last argument is the delay, fixes that. (took me one whole day to fix this after trying eveything).
Something is wrong I think with detecing the div perhaps
Since I struggled with this for awhile I am posting this for anyone interested in making these charts dynamic in Angular 2+. I am using Angular 6 but I think this works in 5 too. The challenge was binding these charts in multiple angular material tabs. The solution was subscribing to event emitters and not using any inputs or lifecycle event in the chart component and instead hooking into the viewchild:
navigation.component.ts:
import { Component, ViewEncapsulation, Output, EventEmitter, ViewChild } from '@angular/core';
import { MatTabChangeEvent } from '@angular/material';
@Component({
selector: 'navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.css'],
encapsulation: ViewEncapsulation.None
})
export class NavigationComponent {
constructor() {}
@Output() navigating: EventEmitter<any> = new EventEmitter();
ChangedTab(event: MatTabChangeEvent){
if (event.tab.textLabel==='Tab 1'){
console.log(event.tab);
this.navigating.emit(event.tab);
}
if (event.tab.textLabel==='Tab 2'){
console.log(event.tab);
this.navigating.emit(event.tab);
}
}
}
navigation.component.html:
<mat-tab-group (selectedTabChange)="ChangedTab($event)">
<mat-tab label="Tab 1">
<componentone [emitchange]="navigating"></componentone>
</mat-tab>
<mat-tab label="Tab 2">
<componenttwo [emitchange]="navigating"></componenttwo>
</mat-tab>
</mat-tab-group>
comonentone.component.ts:
import { Component, OnInit, ViewChild, Input, Output, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs';
import { ChartComponent } from './chart.component';
@Component({
selector: 'componentone',
templateUrl: './componentone.component.html',
styleUrls: ['./componentone.component.css']
})
export class componentone implements OnInit {
constructor() {
}
@Input() emitchange: Observable<any>;
public chartData: any[] = [];
public customColors: any = [
{
"title": "title 1",
"color": "#9a9b9d"
}, {
"title": "title 2",
"color": "#e9700e"
}, {
"title": "title 3",
"color": "#4472b2"
}];
public chartColumns: any =
[
{
"balloonText": "<b>[[title]]</b><br><span style='font-size:14px'>[[category]]: <b>$[[value]]</b></span>",
"fillAlphas": 1,
"title": "title 1",
"type": "column",
"fillColors": "#9a9b9d",
"valueField": "title1",
"showAllValueLabels": false
},
{
"balloonText": "<b>[[title]]</b><br><span style='font-size:14px'>[[category]]: <b>$[[value]]</b></span>",
"fillAlphas": 1,
"title": "title 2",
"type": "column",
"fillColors": "#e9700e",
"valueField": "title2",
"showAllValueLabels": false
},
{
"balloonText": "<b>[[title]]</b><br><span style='font-size:14px'>[[category]]: <b>$[[value]]</b></span>",
"fillAlphas": 1,
"title": "title 3",
"type": "column",
"fillColors": "#4472b2",
"valueField": "title3",
"showAllValueLabels": false
}
];
@ViewChild(ChartComponent) private _chart;
ngOnInit(){
this.emitchange.subscribe((val) => {
console.log("emitting new results in chart " + val.textLabel);
if (val.textLabel==="Tab 1"){
this.populateData();
this._chart.refreshChart(this.chartData, this.chartColumns, this.customColors);
}
});
}
private populateData() {
this.chartData = [{whateveryouwant}];
}
}
componentone.component.html
<chart></chart>
chart.component.ts:
import { Component, OnDestroy } from '@angular/core';
import { AmChartsService, AmChart } from '@amcharts/amcharts3-angular';
@Component({
selector: 'chart',
templateUrl: './chart.component.html',
styleUrls: ['./chart.component.css'],
providers: [AmChartsService]
})
export class ChartComponent implements OnDestroy {
private chart: AmChart;
constructor(private AmCharts: AmChartsService) { }
private graphsData: Array<any> = new Array<any>();
private customColors?: Array<any> = new Array<any>();
makeOptions(dataProvider: any) {
return {
'type': 'serial',
'theme': 'light',
"legend": {
"position": "top",
"markerSize": 10,
"data": this.customColors ? this.customColors : []
},
'dataProvider': dataProvider,
"valueAxes": [{
"stackType": "regular",
"axisAlpha": 0.3,
"gridAlpha": 0,
"unit": "$",
"unitPosition": "left"
}],
"graphs": this.graphsData ? this.graphsData : [],
"gridAboveGraphs": true,
'chartCursor': {
'categoryBalloonEnabled': false,
'cursorAlpha': 0,
'valueLineEnabled': true,
'valueLineBalloonEnabled': true,
'valueLineAlpha': 0.5,
'fullWidth': true
},
"categoryField": 'title',
"categoryAxis": {
"axisAlpha": 0,
"gridAlpha": 0
},
"export": {
"enabled": true,
"fileName": "customfilename"
}
};
}
refreshChart(data: any, columns: any, colors: any) {
console.log("inside refresh chart");
this.graphsData = columns;
this.customColors = colors;
if (this.chart){
this.AmCharts.updateChart(this.chart,()=>{
if (this.chart.dataProvider){
this.chart.dataProvider = data;
this.chart.graphsData = columns;
this.chart.legend.data = colors;
}
else
{
this.chart = this.AmCharts.makeChart('chartdiv', this.makeOptions(data),10);
}
});
}
else{
console.log("recreating chart");
this.chart = this.AmCharts.makeChart('chartdiv', this.makeOptions(data),10);
}
}
ngOnDestroy() {
if (this.chart)
this.AmCharts.destroyChart(this.chart);
}
}
chart.component.html
<div id="chartdiv"
[style.width.%]="100"
[style.height.px]="500"></div>
And now you have a scalable solution :) Hope this helps somebody
Thanks @zuice32 !
I have a canvasJs chart on a lessonsPage component that was inside a mat-tab
. The chart would not load unless I clicked on that lessonsPage tab before the data arrived from the api. After hours of tinkering I realized the div
with the id='chartContainer' was null at runtime since by default the material tab group opened to a different tab (not the lessonsPage with said chart), hence the chart could not render.
I still could not figure this out until I came across your post. I added a ViewChild for the lessonsPage to the outer component, through which I was able to access my setupChart() func. Now I used your idea to call my setupChart() when the user clicks on the lessonsPage tab. I used a setTimeout with 50ms just to be sure the view is in the DOM before rendering.
Thanks for the great post!