Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MapChart not displayed when build with Angular #1146

Closed
samuel-p opened this issue Apr 4, 2019 · 41 comments
Closed

MapChart not displayed when build with Angular #1146

samuel-p opened this issue Apr 4, 2019 · 41 comments
Assignees
Labels

Comments

@samuel-p
Copy link

samuel-p commented Apr 4, 2019

I have an issue when deploying an angular app using the latest amcharts version (4.3.5).

If I use ng serve to test my app it woks without any problems:

Screenshot working

If I build my app using ng build --prod and deploy it, e.g. with an nginx the MapCharts are not displayed:

Screenshot not working

All other charts are working perfectly.

Using version 4.2.6 the MapCharts are working.

I uploaded my code to this repository.

@martynasma
Copy link
Collaborator

Thanks for the heads up. We're looking into it. Will keep you posted.

@MiracleMike
Copy link

MiracleMike commented Apr 19, 2019

Hello!
I got a related issue with the legend. The alignment of text & values change.

using ng serve it if fine. Left aligned.
image

using ng build --prod. Right aligned and the text and values order are reversed.
image

(edit) I tried on 4.2.6 but I still got the issue
Thanks

@Nashoba38
Copy link

Hi , i have the same issue
with the same kind of code ... on a EarthGlobe.

On local server the worldMap is pretty good but after ng build --prod, we can see only the background of the globe, but the countries (polygons) don't appear ...

Does anyone have the solution ?

@Pauan
Copy link
Collaborator

Pauan commented May 28, 2019

Hey guys, we've been working hard to debug this, but it's a very tough case.

It turns out that it's not actually a bug in amCharts at all, instead it's a bug with Angular. To be specific, it's a bug with this package.

To workaround this, you can add --build-optimizer=false to your package.json, like this:

"scripts": {
  "build": "ng build --prod --build-optimizer=false"
},

@Pauan Pauan closed this as completed May 28, 2019
@Nashoba38
Copy link

Nashoba38 commented May 28, 2019 via email

@Nashoba38
Copy link

It doesn't work :(

@Pauan
Copy link
Collaborator

Pauan commented May 31, 2019

@Nashoba38 Could you please share your code, so we can see what the problem is?

@Pauan Pauan reopened this May 31, 2019
@Pauan
Copy link
Collaborator

Pauan commented Jun 1, 2019

@Nashoba38 I just tested your code, and indeed it is broken with --prod, but it works fine with --prod --build-optimizer=false (which is the solution).

Try deleting your dist dir and compiling again. Also make sure that you're using the latest versions of Angular, Angular CLI, and amCharts.

If that doesn't work, then you'll need to send us your full code, so we can find the problem.

@Nashoba38
Copy link

It still doesn't work for me :/ .

I put my code on gitHub if you want to check

https://github.com/Nashoba38/herpingSite/tree/EssaiProd/AngularSnakes

@Pauan
Copy link
Collaborator

Pauan commented Jun 12, 2019

@Nashoba38 I just tested that repo, and it works fine when using --prod --build-optimizer=false

@Pauan Pauan closed this as completed Jun 12, 2019
@Knacktus
Copy link

Knacktus commented Jul 3, 2019

Hey guys, we've been working hard to debug this, but it's a very tough case.

It turns out that it's not actually a bug in amCharts at all, instead it's a bug with Angular. To be specific, it's a bug with this package.

To workaround this, you can add --build-optimizer=false to your package.json, like this:

"scripts": {
  "build": "ng build --prod --build-optimizer=false"
},

Is there an corressponding Github issue at https://github.com/angular/angular-cli/issues ?

@benjamincharity
Copy link

I'll start by saying I really love amcharts v4. Gorgeous and easy to work with 👍

That being said...Turning off the build optimizer is not a valid solution for most production applications.

I see that this has been closed as 'solved' but I'm not sure we've seen any proof this is actually a build optimizer issue. @Pauan have we actually seen that the build optimizer is mangling data or something? If not, it seems there is likely more digging to be done? (and if it has been seen, please update us so that someone can open the issue in the appropriate repo as @Knacktus pointed out)

@terranoha
Copy link

terranoha commented Jul 23, 2019

If it is a bug in Angular, did AMChart open an issue in Angular? If yes, can we track the issue?
I find this workaround very annoying too.
Many thanks

@Globix
Copy link

Globix commented Aug 19, 2019

I am experiencing this same problem and like @benjamincharity said, turning off the build optimizer is not a valid solution for our project.

Any news? Thx

@piotrgolawski
Copy link

Hello, we cannot disable build-optimizer too. Please fix this bug, in another case, we will be forced to stop using your package and require money back.

@Pauan
Copy link
Collaborator

Pauan commented Aug 29, 2019

@piotrgolawski As we have explained, the bug is not in our code, the bug is with Angular. The bug in Angular affects many packages, not just amCharts. We cannot fix Angular bugs. So you will need to ask Angular to fix their bug.

@piotrgolawski
Copy link

piotrgolawski commented Aug 29, 2019 via email

@Pauan
Copy link
Collaborator

Pauan commented Aug 29, 2019

@piotrgolawski There isn't a bug report specifically for amCharts, because it's very difficult to reproduce (it happens because the build-optimizer removes amCharts and d3 code when it shouldn't).

There are several bugs already open for the build optimizer:

angular/angular-cli#12111

angular/angular-cli#11041

angular/angular-cli#9800

angular/angular-cli#14033

And this is the Angular team's opinion on the build optimizer breaking code:

angular/angular-cli#11439 (comment)

There's a reason --prod is disabled by default.


I'm curious: you claim that you cannot disable build-optimizer, what are the file sizes of your project with --build-optimizer=true and --build-optimizer=false?

@piotrgolawski
Copy link

piotrgolawski commented Aug 30, 2019

If it is not a bug in amCharts, so how your previous version is working well https://github.com/amcharts/amcharts3-angular2 ? It is about importing?

We are using webpack.

@Pauan
Copy link
Collaborator

Pauan commented Aug 30, 2019

@piotrgolawski amCharts V3 doesn't use classes (it is vanilla JS), whereas V4 uses classes a lot (it uses TypeScript).

The issue with the build optimizer is specifically with classes.

In addition, V3 is not processed by Angular or Webpack, it is loaded with <script> tags, so it bypasses everything.

You haven't answered my question about file sizes, but assuming that there really is a huge difference, you can use the <script> version of V4, which will bypass Angular and Webpack, so then you can use the build optimizer.

@benjamincharity
Copy link

This is great information. Thank you for that.

As far as build size, I can only speak for the place I am currently. But I’m on a team that manages our components and libraries, while the applications themselves are built by other teams. So it’s not a choice I can actually make.

Thanks again for the great responses.

@aggiustino
Copy link

Just noticed that this issue isn't happening with amcharts version 4.2.6 with the same version of builder optimiser.
I hope this might be helpful for further debugging.
Unfortunately, setting --build-optimizer=false is not an option for who need to guarantee best app performances.

@Pauan
Copy link
Collaborator

Pauan commented Sep 16, 2019

@aggiustino That's because in version 4.3.0 we started using d3-geo, which significantly improved the functionality of maps.

@ha-akelius
Copy link

Unfortunately, setting --build-optimizer=false is not an option for who need to guarantee the best app performances.

Thank you @aggiustino, Unfortunately, I had to downgrade to 4.2.6 :(
and I would like to use the latest version of amchart

@Pauan so amchart have issues for angular after upgrading to d3-geo,
my question: can we not use it (d3-geo)? until angular fix this issue
I can't upgrade (so I don't have the latest features)

or any solution it may work

@aggiustino
Copy link

@Pauan is there any plan for fixing this issue?

@Pauan
Copy link
Collaborator

Pauan commented Nov 23, 2019

@ha-akelius d3-geo is important for the functionality of the maps, so until Angular fixes their bugs, you will have to disable the build optimizer by using --build-optimizer=false.

@aggiustino Angular has made it clear that they don't care if their build optimizer is buggy and breaks third-party libraries, so I doubt they will fix it anytime soon.

@myatthu-mm
Copy link

It doesn't work :(

yes, it doesn't work.

@devtronic
Copy link

I also struggled with this problem and found a solution (tested with Angular 8):
Install this: https://github.com/just-jeb/angular-builders/tree/master/packages/custom-webpack

Update your angular.json to use a modified config for webpack (replace builder and add customWebPackConfig to options):

"builder": "@angular-builders/custom-webpack:browser",
"options": {
  "customWebpackConfig": {
    "path": "./custom-webpack.config.js",
    "replaceDuplicatePlugins": true
  }
}

Create a custom-webpack.config.js.

// ./custom-webpack.config.js
module.exports = (config, options) => {
  config.optimization.minimizer.filter(({constructor: {name}}) => name === 'TerserPlugin')
    .forEach(terser => {
      Object.assign(terser.options.terserOptions, {
        compress: {
          global_defs: {ngDevMode: false, ngI18nClosureMode: false},
        },
      });
    });

  return config;
};

@msrumon
Copy link

msrumon commented May 2, 2020

Mine is even not working on local server. Used the exact same code from this link, except that I placed the demo data to MongoDB, fetch them inside ngOnInit() hook and store them inside this.chart.data, other things are pretty much the same as what @samuel-p did. Below is my component typescript file:

import {
  Component,
  NgZone,
  OnInit,
  AfterViewInit,
  OnDestroy,
} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import * as socketio from 'socket.io-client';

import * as m4Core from '@amcharts/amcharts4/core';
import * as m4Maps from '@amcharts/amcharts4/maps';
import AnimatedTheme from '@amcharts/amcharts4/themes/animated';
import WORLD_MAP from '@amcharts/amcharts4-geodata/worldLow';

import { environment } from '../../environments/environment';

m4Core.useTheme(AnimatedTheme);

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss'],
})
export class MapComponent implements OnInit, AfterViewInit, OnDestroy {
  private readonly URI = environment.api.uri + environment.api.endpoints.map;
  private readonly socket = socketio(environment.socket);
  private chart: m4Maps.MapChart;

  constructor(
    private readonly zone: NgZone,
    private readonly http: HttpClient
  ) {}

  private animateBullet(circle) {
    const animation = circle.animate(
      [
        { property: 'scale', from: 1, to: 5 },
        { property: 'opacity', from: 1, to: 0 },
      ],
      1000,
      m4Core.ease.circleOut
    );
    animation.events.on('animationended', (event) => {
      this.animateBullet(event.target.object);
    });
  }

  ngOnInit() {
    // add data
    const colorSet = new m4Core.ColorSet();
    this.http
      .get<{ statusCode: number; body: string }>(this.URI)
      .pipe(
        map((resp) => {
          const { body } = resp;
          const array = JSON.parse(body);
          const data = array.map((each) => ({
            title: each.name,
            latitude: each.latitude,
            longitude: each.longitude,
            color: colorSet.next(),
          }));
          return data;
        })
      )
      .subscribe((data) => {
        this.chart.data = data;
      });

    // listen for changes
    this.socket.on('map-replace', (change) => {
      const temp = this.chart.data;
      const index = temp.findIndex(
        (each) =>
          each.latitude === change.updatedDocument.latitude &&
          each.longitude === change.updatedDocument.longitude
      );
      temp[index].title = change.updatedDocument.name;
      this.chart.data = temp;
    });
  }

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
      // Create map instance
      const mapChart = m4Core.create('map', m4Maps.MapChart);

      // Set map definition
      mapChart.geodata = WORLD_MAP;

      // Set projection
      mapChart.projection = new m4Maps.projections.Miller();

      // Create map polygon series
      const pgSeries = mapChart.series.push(new m4Maps.MapPolygonSeries());

      // Exclude Antartica
      pgSeries.exclude = ['AQ'];

      // Make map load polygon (like country names) data from GeoJSON
      pgSeries.useGeodata = true;

      // Configure series
      const pgTemplate = pgSeries.mapPolygons.template;
      pgTemplate.tooltipText = '{name}';
      pgTemplate.polygon.fillOpacity = 0.6;

      // Create hover state and set alternative fill color
      const hs = pgTemplate.states.create('hover');
      hs.properties.fill = mapChart.colors.getIndex(0);

      // Add image series
      const imgSeries = mapChart.series.push(new m4Maps.MapImageSeries());
      imgSeries.mapImages.template.propertyFields.longitude = 'longitude';
      imgSeries.mapImages.template.propertyFields.latitude = 'latitude';
      imgSeries.mapImages.template.tooltipText = '{title}';
      imgSeries.mapImages.template.propertyFields.url = 'url';

      const circle1 = imgSeries.mapImages.template.createChild(m4Core.Circle);
      circle1.radius = 3;
      circle1.propertyFields.fill = 'color';

      const circle2 = imgSeries.mapImages.template.createChild(m4Core.Circle);
      circle2.radius = 3;
      circle2.propertyFields.fill = 'color';

      circle2.events.on('inited', (event) => {
        this.animateBullet(event.target);
      });

      this.chart = mapChart;
    });
  }

  ngOnDestroy() {
    this.zone.runOutsideAngular(() => {
      if (this.chart) {
        this.chart.dispose();
      }
    });
  }
}

@supriyaKrishnamurthy
Copy link

It doesn't work :(

yes, it doesn't work.

It worked for me, try doing the same for postinstall as well
"build": "ng build --prod --build-optimizer=false",

"postinstall": "ng build --aot --prod --build-optimizer=false",

@mg1075
Copy link

mg1075 commented Jul 14, 2020

Is this build-optimizer still an issue with the latest versions of angular?

I was scratching my head when I saw the amcharts documentation for angular read:
--build-optimizer=false
https://www.amcharts.com/docs/v4/getting-started/integrations/using-angular2/

I don't have much experience with Angular 2+, but if the production file size has the chance of being significantly impacted by setting `--build-optimizer=false", this could be very important.

What are the viable workarounds?
Can the amcharts documentation be updated with those workarounds?

@wallace41290
Copy link

I am running

"@amcharts/amcharts4": "^4.9.30",
"@amcharts/amcharts4-geodata": "^4.1.16",
"@angular/core": "~9.1.12"

with the same problems, no map displayed. Turning off build optimization increases my build size by 2mb, so very significant for me, about (75%). This seems like a really poor solution. I have a simple app here, but other applications that are much larger, if I turn off optimization there, the consequences would be unacceptable.

Would love it if there was a specific issue filed in the optimizer github if there really is a problem there.

Has anyone found a better solution?

@mg1075
Copy link

mg1075 commented Jul 15, 2020

@wallace41290 Have you tried the lazy loading approach, and does that bypass the need to set --build-optimizer=false?
Would be interested to know what results you get.

"Lazy loading amCharts
Check out this tutorial for information on how you can lazy-load amCharts modules on-demand, rather than compiling them into your app."
https://www.amcharts.com/docs/v4/getting-started/integrations/using-angular2/
https://www.amcharts.com/docs/v4/tutorials/lazy-loading-amcharts-modules/

@wallace41290
Copy link

@mg1075 Just tried it, some problem, no chart on prod build

@dittops
Copy link

dittops commented Aug 26, 2020

ng build --prod --build-optimizer=false

Worked for me as well.

@wallace41290
Copy link

ng build --prod --build-optimizer=false

Worked for me as well.

Maybe I am misunderstanding, but I can build it with build-optimizer set to false. But that is not an acceptable solution and I would be forced to use a different charting library. I work in very large enterprise applications, and the build size and performance cost is too great to just turn the build optimizer off.

@jsanta
Copy link

jsanta commented Sep 16, 2020

We recently faced this issue (still an issue). As the officially recommended solution is not an option, because our app size is already too large we solved it by lazy loading amcharts AND explicitly separating amchart scripts using webpack magic comments. Not exactly what we use but. it may give you an idea:

  import(/* webpackChunkName: "amcharts" */ "@amcharts/amcharts4/core"),
  import(/* webpackChunkName: "amcharts" */ "@amcharts/amcharts4/charts"),
  import(/* webpackChunkName: "amcharts" */ "@amcharts/amcharts4/themes/animated")
]).then((modules) => {
  const am4core = modules[0];
  const am4charts = modules[1];
  const am4themes_animated = modules[2].default;

  // Chart code goes here
}).catch((e) => {
  console.error("Error when creating chart", e);
}) 

This will generate a separate chunk called amcharts.js

For a detailed explanation read this article on Angular: Dynamically immporting large libraries

@webPlayer7
Copy link

ng build --prod --build-optimizer=false
I had problems both for mapchart and piechart left align.
Above code solved my problem

@Hammarskjold
Copy link

Hammarskjold commented May 28, 2021

We had this issue, and it was driving us nuts. We worked around it by lazy loading the amchart stuff where maps were needed and then referencing like this to keep the TypeScript logic intact:

private am4maps: typeof import("@amcharts/amcharts4/maps");
private am4core: typeof import("@amcharts/amcharts4/core");

imageSeries = null;
private GeoDataWorldLow = null;
constructor(private zone: NgZone) {

}

ngOnInit() {

}

ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
        this.hasRunOutsideAngular = true;
        //If no options we you ignore creating chart
        if (!this._options) {
            return;
        }

        //Lazy loading of the AMCharts maps, as Angular optimizer breaks it.
        Promise.all([
            import(/* webpackChunkName: "am4core" */ '@amcharts/amcharts4/core'),
            import(/* webpackChunkName: "am4maps" */ '@amcharts/amcharts4/maps'),
            import(/* webpackChunkName: "amcharts" */ '@amcharts/amcharts4-geodata/worldLow'),
        ]


        ).then((modules) => {

            this.am4core = modules[0];
            this.am4maps = modules[1];
            this.imageSeries = this.am4maps.MapImageSeries;

            this.GeoDataWorldLow = modules[2].default;

            this.chart = this.createChart(this.host.nativeElement, this._options);
            this.assignValue(this.chart, this.data);

        }).catch((e) => {
            console.error("Error when creating chart", e);
        })


    });
}

We then call the underlying loaded as per:

private createChart(id: string, options: MapChartOptions) {
const chart = this.am4core.create(id, this.am4maps.MapChart);

    chart.geodata = this.GeoDataWorldLow;
    chart.projection = new this.am4maps.projections.Miller();

Not pretty, but better than disabling the optimizer.

//A

@MayaraFreitas
Copy link

We had this issue, and it was driving us nuts. We worked around it by lazy loading the amchart stuff where maps were needed and then referencing like this to keep the TypeScript logic intact:

private am4maps: typeof import("@amcharts/amcharts4/maps");
private am4core: typeof import("@amcharts/amcharts4/core");

imageSeries = null;
private GeoDataWorldLow = null;
constructor(private zone: NgZone) {

}

ngOnInit() {

}

ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
        this.hasRunOutsideAngular = true;
        //If no options we you ignore creating chart
        if (!this._options) {
            return;
        }

        //Lazy loading of the AMCharts maps, as Angular optimizer breaks it.
        Promise.all([
            import(/* webpackChunkName: "am4core" */ '@amcharts/amcharts4/core'),
            import(/* webpackChunkName: "am4maps" */ '@amcharts/amcharts4/maps'),
            import(/* webpackChunkName: "amcharts" */ '@amcharts/amcharts4-geodata/worldLow'),
        ]


        ).then((modules) => {

            this.am4core = modules[0];
            this.am4maps = modules[1];
            this.imageSeries = this.am4maps.MapImageSeries;

            this.GeoDataWorldLow = modules[2].default;

            this.chart = this.createChart(this.host.nativeElement, this._options);
            this.assignValue(this.chart, this.data);

        }).catch((e) => {
            console.error("Error when creating chart", e);
        })


    });
}

We then call the underlying loaded as per:

private createChart(id: string, options: MapChartOptions) {
const chart = this.am4core.create(id, this.am4maps.MapChart);

    chart.geodata = this.GeoDataWorldLow;
    chart.projection = new this.am4maps.projections.Miller();

Not pretty, but better than disabling the optimizer.

//A

Worked for me!!!
Definitely is better than disabling the optimizer!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests