2

I am using ngx-leaflet, when adding markers to the map, the application slows down due to the large amount of Function Call (zone.js). I tried to use changeDetection and ngZone but to no avail. Please help :)

 constructor(zone: NgZone) {}

onMapReady(map: L.Map) {
    this.map = map;
    this.zone.run(() => {
      this.getPoints();
    });
    L.control.scale({position: 'bottomleft'}).addTo(this.map);
    L.control.zoom({position: 'bottomleft'}).addTo(this.map);
    this.createLegend();
  }

 private updateLayers(pointList: Point[]) {
    this.layers = [];
    let group = L.featureGroup();
    for (let point of pointList) {
      if (point.gps) {
        this.zone.run( ()=> {
          let marker: Marker = L.marker([point.gps.latitude, point.gps.longitude], {icon: this.setIcon(point.status)});
          group.addLayer(marker);
          this.setPopupContent(marker, point);
          this.layers.push(marker);
          this.layers = this.layers.slice();
          this.changeDetector.detectChanges();
        });
      }
    }
    if (pointList.length != 0) {
      this.zone.run(()=> {
        this.leafletDirective.getMap().fitBounds(group.getBounds(), {
          padding: [10, 10],
          animate: false,
          duration: 0.01,
          noMoveStart: true});
      });
    }
  }

enter image description here

Blackboy
  • 192
  • 2
  • 20

1 Answers1

3

You shouldn't need to use zone.run() inside of the onMapReady function if that function is being called by the leaflet directive output binding. That call will already be in the Angular zone.

Then, it looks like you're adding bunch of markers to a feature group in order to get a bounding box. But, you're also adding all the markers to the layers array. You could just add the featureGroup to the layers array and simplify your code a bit.

Also, you can either build the new layers array and then just run the actual change to the layers array in the Angular zone. That way, it's only called once per update (as opposed to once per marker).

constructor(zone: NgZone) {}

onMapReady(map: L.Map) {
  this.map = map;
  this.getPoints();

  L.control.scale({position: 'bottomleft'}).addTo(this.map);
  L.control.zoom({position: 'bottomleft'}).addTo(this.map);
  this.createLegend();
}

private updateLayers(pointList: Point[]) {
  let group = L.featureGroup();

  pointList.forEach((point: Point) => {
    if (point.gps) {
        let marker = L.marker(
          [point.gps.latitude, point.gps.longitude],
          {icon: this.setIcon(point.status)});
        group.addLayer(marker);
        this.setPopupContent(marker, point);
      }
  });

  if (pointList.length != 0) {
    this.leafletDirective.getMap().fitBounds( group.getBounds(), {
      padding: [10, 10],
      animate: false,
      duration: 0.01,
      noMoveStart: true
    });
  }

  // The change to the input bound layers array is the only thing
  // that needs to be run in the angular zone
  this.zone.run(() => {
    this.layers = [ group ];
  });

}
reblace
  • 4,115
  • 16
  • 16
  • Thank you for your help now everything runs smoothly and does not stutter. I have one more question, whether I should always add a popup when creating markers or dopeiro after clicking on a marker. My popup is an angular component. And I'm going to draw up to 500 markers – Blackboy Jun 05 '18 at 08:04
  • 1
    That depends on the behavior you want and how much work you want to put into it. The easiest path is to do what you're doing. But, that doesn't provide any dynamic binding. If you want dynamic binding, you could have one component popup that you move around or you could use component injection in Angular. With component injection you can combine dynamic binding and show more than one popup on the screen at once. – reblace Jun 05 '18 at 14:46
  • Thanks again, you've helped me a lot – Blackboy Jun 05 '18 at 14:53