amcharts / amcharts3-angular2

Official Angular 2 plugin for amCharts V3
99 stars 35 forks source link

Charts are not shown on the page #12

Open imfarhad opened 7 years ago

imfarhad commented 7 years ago

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

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

`

calloncampbell commented 7 years ago

I have same issue after following instructions.

peluprvi commented 7 years ago

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, {
      // ...
    }
    // ...
  }
   //...
}
chetan-rathod commented 6 years ago

i have same issue

g-arakelov commented 6 years ago

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

aslampeerbits commented 6 years ago

just try this

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

ngAfterViewInit() {
setTimeout(()=>{    
AmCharts.makeChart('chartdiv', {
}
 },1000);
}
jdlopezq commented 6 years ago

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

sebastienschoepfer commented 6 years ago

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 ?

natejgardner commented 6 years ago

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

jimmykane commented 6 years ago

@calloncampbell Hi,

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

jimmykane commented 6 years ago

@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

zuice32 commented 5 years ago

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

Avramo commented 3 years ago

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!