geoman-io / leaflet-geoman

πŸ‚πŸ—ΊοΈ The most powerful leaflet plugin for drawing and editing geometry layers
https://geoman.io
MIT License
2.22k stars 433 forks source link

ERROR TypeError: Cannot read properties of undefined (reading 'addControls') in Angular 13 #1234

Closed TheTiGuR closed 2 years ago

TheTiGuR commented 2 years ago

Similar to #1220 (and have also had that problem), I am running into map.pm being undefined, and I am not sure why. I created a demo project as a POC and it worked great. I take that same code into my actual project and map.pm always ends up being undefined. Leaflet is imported, then geoman at the head of the ts file, and then my map is init'd the exact same way as my POC, but no dice. Would love any suggestions on where to look.

    this.map = map(this.mapId, {
      center: [39.8282, -98.5795],
      zoom: 3,
    });
    const tiles = tileLayer(
      'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      {
        maxZoom: 18,
        minZoom: 3,
        attribution:
          '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
      }
    );

    tiles.addTo(this.map);
    control.scale().addTo(this.map);
    this.featureGroup.addTo(this.map);
    function logEvent(e: any) {
      console.log(e);
    }
    this.map.pm.addControls({})

This fails at the addControls() in the real project, but not in POC, with the only difference between real and POC and the mapId. A map is displayed with scale and +/- controls.

Edit: Trying to get pm working, I tried PM.reInitLayer which also failed with PM being undefined.

Falke-Design commented 2 years ago

How do you include / import Leaflet-Geoman & Leaflet?

TheTiGuR commented 2 years ago

Geoman is imported directly after leaflet, and leaflet is the second import in the component:

import {
  Map,
  map,
} from 'leaflet';
import '@geoman-io/leaflet-geoman-free';

I have also tried importing leaflet as import * as L from 'leaflet'

The results are the same either way in my non-working project, but also work fine in my proof of concept project. This includes of course updating all the references with L. .

Falke-Design commented 2 years ago

Geoman should apply to the gloabl L, so I think you should use import * as L from 'leaflet'. Maybe window.L = L before importing geoman helps.

Is it possible to share your project and POC?

TheTiGuR commented 2 years ago

My project, no, but here is my POC: POC

As far as it goes, I have copied all the leaflet pieces in this component in the POC and dropped in place in my actual project, and no luck. In this POC I don't use L, and all works fine. I have compared searching this project and my live project to compare just search results for 'geoman' and compared, and everything is exactly the same. I'm sure there is something dumb on my end that I missed, but for the life of me see it.

In the meantime, I will try what you suggested with window.L and see what happens. Thanks for looking!

Falke-Design commented 2 years ago

Do you have included other Leaflet plugins? Once I had a problem with including multiple plugins. One of plugins replaced the modified L (with Geoman) with its own Leaflet context without Geoman.

TheTiGuR commented 2 years ago

Finally got to a point to look at this - I did have a couple other plugins (leaflet-draw among them) that I am intending to replace with Geoman, but even fully removing them does not seem to fix it. As best I can tell, I only have leaflet itself and Geoman as leaflet related plugins, but it still gives me undefined for pm when I try and access it.

Some additional context of the component, if it helps:

My imports, other than angular itself, and my own project pieces

import { Geometry } from '@turf/turf';
import { Feature } from '@turf/helpers';
import {
  Map,
  FeatureGroup,
  geoJSON,
  LeafletMouseEvent,
  map,
  tileLayer,
  icon,
  IconOptions,
  Marker,
  control,
} from 'leaflet';
import '@geoman-io/leaflet-geoman-free';

And, simplified to barest failing state, the initMap() function being called from ngAfterViewInit (same as my POC):

  private initMap(): void {
    this.map = map(this.mapId, {
      center: [40, -100],
      zoom: 3,
      layers: [this.openStreetLayer()],
    });
    this.map.pm.addControls({});
    control.scale().addTo(this.map);
    control.layers(this.baseMaps, {}).addTo(this.map);
Falke-Design commented 2 years ago

What if you import leaflet like import * as L from 'leaflet' instead of:

import {
  Map,
  FeatureGroup,
  geoJSON,
  LeafletMouseEvent,
  map,
  tileLayer,
  icon,
  IconOptions,
  Marker,
  control,
} from 'leaflet';

And do you import leaflet / geoman multiple times?

It's hard to help if the POC works ... πŸ˜„

TheTiGuR commented 2 years ago

I have done that previously in testing, and tried it again just to be make sure I did, but still the same result.

And yeah, it's why I'm here, because it's really annoying that it doesn't. Just hoping that someone smarter than me might have a better rabbit hole to go down that i haven't tried already.

Here's what I have right now:

  private initMap(): void {
    this.map = L.map(this.mapId, {
      center: [40, -100],
      zoom: 3,
      layers: [this.openStreetLayer()],
    });
    this.map.pm.addControls({});
    L.control.scale().addTo(this.map);
    L.control.layers(this.baseMaps, {}).addTo(this.map);
  }

Again with pm being undefined.

Falke-Design commented 2 years ago

Please share your package.json / versions of leaflet and all plugins

TheTiGuR commented 2 years ago
  "dependencies": {
    "@angular/animations": "13.3.11",
    "@angular/cdk": "13.3.9",
    "@angular/common": "13.3.11",
    "@angular/compiler": "13.3.11",
    "@angular/core": "13.3.11",
    "@angular/forms": "13.3.11",
    "@angular/platform-browser": "13.3.11",
    "@angular/platform-browser-dynamic": "13.3.11",
    "@angular/router": "13.3.11",
    "@geoman-io/leaflet-geoman-free": "^2.13.0",
    "@ng-idle/core": "^11.1.0",
    "@ng-idle/keepalive": "^11.0.3",
    "@turf/turf": "^6.5.0",
    "@zxcvbn-ts/core": "^2.0.3",
    "@zxcvbn-ts/language-common": "^2.0.1",
    "@zxcvbn-ts/language-en": "^2.0.1",
    "angular-password-strength-meter": "^4.3.1",
    "chart.js": "^3.8.2",
    "coordinator": "^0.5.0",
    "dayjs": "^1.11.4",
    "file-saver": "^2.0.5",
    "guid-typescript": "^1.0.9",
    "leaflet": "^1.7.1",
    "primeflex": "^3.1.3",
    "primeicons": "^5.0.0",
    "primeng": "^13.4.1",
    "prismjs": "^1.27.0",
    "ramda": "^0.28.0",
    "rxjs": "^7.5.6",
    "tslib": "^2.3.1",
    "zone.js": "^0.11.7",
    "zxcvbn3": "^0.1.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^13.3.9",
    "@angular-eslint/builder": "^13.5.0",
    "@angular-eslint/eslint-plugin": "^13.5.0",
    "@angular-eslint/eslint-plugin-template": "^13.5.0",
    "@angular-eslint/schematics": "^13.5.0",
    "@angular-eslint/template-parser": "^13.5.0",
    "@angular/cli": "13.3.9",
    "@angular/compiler-cli": "13.3.11",
    "@fortawesome/fontawesome-free": "^6.1.2",
    "@types/jasmine": "^4.0.2",
    "@types/jasminewd2": "~2.0.8",
    "@types/leaflet": "^1.7.9",
    "@types/node": "^17.0.45",
    "@types/ramda": "^0.28.15",
    "@typescript-eslint/eslint-plugin": "^5.32.0",
    "@typescript-eslint/parser": "^5.32.0",
    "codelyzer": "^6.0.0",
    "eslint": "^8.21.0",
    "eslint-plugin-import": "2.26",
    "eslint-plugin-prefer-arrow": "^1.2.3",
    "jasmine-core": "^4.3.0",
    "jasmine-spec-reporter": "^7.0.0",
    "karma": "^6.3.4",
    "karma-chrome-launcher": "^3.1.0",
    "karma-coverage-istanbul-reporter": "^3.0.3",
    "karma-jasmine": "^4.0.1",
    "karma-jasmine-html-reporter": "^1.7.0",
    "ts-node": "^10.9.1",
    "typescript": "~4.6.3"
  }
TheTiGuR commented 2 years ago

So, having fiddled and fiddled and fiddled.... I saw somewhere someone did a "console.log(L.PM); console.log(L.map.pm)" to get PM initialized. I tried it. It worked. I removed those logs. It still worked. I put it all back to the L import, rebuilt a few times, reload after clearing cache, still seems to be working. I assume I caught something magic in there somewhere that I missed before, but man, I wish I knew what. Thanks for prodding at it with me!

noway commented 9 months ago
import L from 'leaflet'
(window as any).L = L

for imports and

console.log(L.PM); console.log((L.map as any).pm)

before the L.map() initialisation did the trick for me πŸ‘

thanks @Falke-Design and @TheTiGuR