amcharts3-angular2 icon indicating copy to clipboard operation
amcharts3-angular2 copied to clipboard

Charts are not shown on the page

Open imfarhad opened this issue 8 years ago • 12 comments

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>

Factschart Loading... `

imfarhad avatar Jan 13 '17 23:01 imfarhad

I have same issue after following instructions.

calloncampbell avatar Feb 02 '17 18:02 calloncampbell

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, {
      // ...
    }
    // ...
  }
   //...
}

peluprvi avatar Apr 01 '17 06:04 peluprvi

i have same issue

chetan-rathod avatar Feb 23 '18 08:02 chetan-rathod

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">

g-arakelov avatar Feb 26 '18 10:02 g-arakelov

just try this

import { AmChart } from '@amcharts/amcharts3-angular';
import { AmChartsModule } from '@amcharts/amcharts3-angular/amcharts.directive';

ngAfterViewInit() {
setTimeout(()=>{    
AmCharts.makeChart('chartdiv', {
}
 },1000);
}

aslampeerbits avatar Mar 09 '18 12:03 aslampeerbits

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

jdlopezq avatar Mar 16 '18 16:03 jdlopezq

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 ?

sebastienschoepfer avatar Apr 04 '18 15:04 sebastienschoepfer

Having the same problem. None of the solutions here work for me.

natejgardner avatar Apr 11 '18 19:04 natejgardner

@calloncampbell Hi,

Inside the new material tabs for angular the chart does not draw after the view init.

jimmykane avatar Apr 22 '18 18:04 jimmykane

@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

jimmykane avatar Apr 22 '18 18:04 jimmykane

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

zuice32 avatar Nov 11 '18 07:11 zuice32

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!

Avramo avatar Jun 20 '21 12:06 Avramo