Search code examples
angularleafletmapszone.jsngx-leaflet

Ngx-leaflet Leaflet, angular 5 lot of Function Call from zone.js


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


Solution

  • 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 ];
      });
    
    }