ERROR TypeError: Cannot read property '_leaflet_pos' of undefined

8.7k Views Asked by At

I have angular 10 app using ngx-leaflet and routing. I have a map component, which dynamically displays custom markers on map, based on user selection. I navigate from map component view to another component. Then I navigate back to map component. User can change date, and based on that, old layer of markers is removed and new layer of markers is loaded and shown. Everything works fine, but I always get this error:

ERROR TypeError: Cannot read property '_leaflet_pos' of undefined
at getPosition (leaflet-src.js:2450)
at NewClass._getMapPanePos (leaflet-src.js:4439)
at NewClass._moved (leaflet-src.js:4443)
at NewClass.getCenter (leaflet-src.js:3798)
at NewClass.setZoom (leaflet-src.js:3181)
at SafeSubscriber._next (map.component.ts:960)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)

I can reproduce this error only when I go back to the map component. If i stay only at the map component no error is shown. I've searched for fix, but from what I found it seems nobody really knows why is this happening and how to fix this error. I've found these two issues on GitHub dealing with the same problem in Vue.js, so I guess it's problem with leaflet itself, and not ngx-leaflet.

https://github.com/vue-leaflet/Vue2Leaflet/issues/613

https://github.com/stefanocudini/leaflet-search/issues/129

I've tried to change this:

  function getPosition(el) {
// this method is only used for elements previously positioned using setPosition,
// so it's safe to cache the position for performance

return el._leaflet_pos || new Point(0, 0);

}

to this:

 function getPosition(el) {
// this method is only used for elements previously positioned using setPosition,
// so it's safe to cache the position for performance

if(el){
    return el._leaflet_pos || new Point(0, 0);
    }
    else{
    return new Point(0, 0);
    }

}

But then the error just looks like this:

ERROR TypeError: Cannot set property '_leaflet_pos' of undefined
at setPosition (leaflet-src.js:2433)
at NewClass._resetView (leaflet-src.js:4154)
at NewClass.setView (leaflet-src.js:3174)
at NewClass.setZoom (leaflet-src.js:3186)
at SafeSubscriber._next (map.component.ts:960)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)
at MapSubscriber._next (map.js:35)

UPDATE: As @pk. suggested in comments ,when I don't call setZoom or call it before I remove old markers I get this error:

ERROR TypeError: Cannot read property 'appendChild' of undefined
at NewClass._initIcon (leaflet-src.js:7608)
at NewClass._initIcon (leaflet.rotatedMarker.js:23)
at NewClass.onAdd (leaflet-src.js:7460)
at NewClass._layerAdd (leaflet-src.js:6572)
at NewClass.whenReady (leaflet-src.js:4433)
at NewClass.addLayer (leaflet-src.js:6634)
at NewClass.eachLayer (leaflet-src.js:6861)
at NewClass.onAdd (leaflet-src.js:6845)
at NewClass._layerAdd (leaflet-src.js:6572)
at NewClass.whenReady (leaflet-src.js:4433)

UPDATE 2: When I don't add new marker layer to map (this.deskLayer.addTo(map)), the error dissapears, but I want to add new markers to map... This is what happens when user changes date:

onMapReady(map: Map) {

//listening for USER DATE CHANGE
this.userFloorService.sharedUserSelectedDate
  .pipe(skip(1))
  .subscribe(() => {
    this.deskLayer.remove(); // first remove old desks
    this.userFloorService // then get desks and reservations
      .getFloor(this.floorNumber)
      .subscribe( (data) => { 
        // create new reservations
        let reservationsArr = data.records[0].reservations;
        // create new DESKS
        let deskMarkers = [];
        data.records[0].desks.forEach((desk) => {
          let deskId = desk.desk_id;
          let deskMarker = marker(
            [
              desk.desk_coordinate.coordinates[0],
              desk.desk_coordinate.coordinates[1],
            ],
            {
              title: this.getDeskTitle(desk, reservationsArr), // set desk title to RESERVED/FREE
              rotationAngle: desk.desk_angle,
              rotationOrigin: 'center center',
              riseOnHover: true
            }
          ).on('click', () => {
            this.zone.run(() => {
              this.openDeskDialog(deskMarker.options.title,deskId); 
            });
          });
          deskMarker.setIcon(this.getDeskIcon(deskMarker)); // for displaying desk icons on zoomLvl -1.5
          deskMarkers.push(deskMarker);
        });
        this.deskLayer = layerGroup(deskMarkers); // add new desks to deskLayer
        this.layersControl.overlays['Desks'] = this.deskLayer; // reassign desks in overlays for correct desk layer toggling 

        this.deskLayer.addTo(map); 
        map.setZoom(-1); // set zoom
      },
      error =>{
        console.log(error);
      }
      ); 
  });
4

There are 4 best solutions below

2
hawran On BEST ANSWER

It turned out, that these errors were happening because I used BehaviorSubject to pass data between components, and everytime I navigated out and back to the map component, new subscription to BehaviorSubject was created without destroying the old subscribtion. So destroying subscriptions everytime I navigated from map component solved it. Maybe this will help to somebody.

0
Mohammad Reza On

I had this error too. I added ? to part of the code in node_modules to fix the problem.

open node_modules/leaflet/dist/leaflet-src.js and change

function getPosition(el) {
    // this method is only used for elements previously positioned using setPosition,
    // so it's safe to cache the position for performance

    return el._leaflet_pos || new Point(0, 0);
  }

to

function getPosition(el) {
    // this method is only used for elements previously positioned using setPosition,
    // so it's safe to cache the position for performance

    return el?._leaflet_pos || new Point(0, 0);
  }
0
Danil On

I have resizeObserver, so when I hide map observer still working. The trick was to disconnect it on unmount:

export function MapPositionChange() {

  const parentMap = useMap();

  useEffect(() => {

    var container = document.getElementById('map-container')

    const resizeObserver = new ResizeObserver(() => {
      parentMap.invalidateSize();
    });
    resizeObserver.observe(container);

    return () => {
      resizeObserver.unobserve(container);
      resizeObserver.disconnect();
    };
  }, [parentMap]);
0
C RICH On

I had a similar issue when using the leaflet map on(listers) method to render a minimap. Make sure to call the off(listeners) when the component is unmounted.

  const onChange = useCallback(() => {
    ...
  }, [...]);

  // Listen to events on the parent map
  const handlers = useMemo(() => ({ move: onChange, zoom: onChange }), []);
  props.parentMap.on(handlers);

  //Unsubscribe on unmount
  useEffect(
    () => () => {
      props.parentMap.off(handlers);
    },
    [props.parentMap, handlers],
  );