Warm tip: This article is reproduced from serverfault.com, please click

Angular 10

发布于 2020-11-18 22:09:51

This is about the compatibility issue between Webpack and Leaflet. As you may already know (github Issue, SO-Question1, SO-Question2 npm plugin against the issue, ngx-leaflet readme) leaflet manipulates its URLs to images in a way that is incompatible with webpack. If left unfixed they produce nonsense urls such as http://localhost:8000/static/frontend/marker-icon.2b3e1faf89f94a483539.png%22)marker-icon.png as opposed to http://localhost:8000/static/frontend/marker-icon.2b3e1faf89f94a483539.png. I can fix this with some solutions on my dev-environment, but not in my prod builds.

I have a single component that does nothing else but build a leaflet map. I have tried the given answers, I have tried the plugin, I remain without markers in my builds.

What should I be doing? What is my mistake?

My Setup

Generally I seek to deploy angular on a django backend server. All urls under "/api" belong to the Backend API, all urls under "/frontend" belong to my Angular frontend.

Leaflet is imported in my angular.json, but only its js file.

//angular.json
...
            "styles": [
              "src/styles.scss",
              "node_modules/font-awesome/css/font-awesome.css"
            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.js",
              "node_modules/popper.js/dist/umd/popper.js",
              "node_modules/leaflet/dist/leaflet.js",
              "node_modules/tinymce/tinymce.min.js"
            ]
...

Sidenote: I am currently not using ngx-leaflet as I didn't see a reason to. I did however read up on fixes that were using ngx-leaflet as the issue lies in the general webpack-leaflet interaction as far as I understand it.

In my component I import leaflet with import * as L from "node_modules/leaflet"; as well as its Marker class specifically with import { Marker } from 'leaflet';. Using none of the fixes below I get markers on ng serve, but not in ng build --prod.

Now to my results for every solution. I looked at every solution with both ng serve and ng build --prod, the latter of which I ran on the dev-environment for my django backend server. Bar solution 3 they always had the same results:

1. Use leaflet-defaulticon-compatibility-plugin

//leaflet-map.component.ts
import { Marker } from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css'; // Re-uses images from ~leaflet package
import * as L from "node_modules/leaflet";
import 'leaflet-defaulticon-compatibility';

This changed the url to the standard marker to "undefined": enter image description here Markers did not load at all.

2. Explicitly set default icon image resource

//leaflet-map.component.ts
import { Marker } from 'leaflet';
import * as L from "node_modules/leaflet";

delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
});

This caused parts of the URL to be replaced by an object: enter image description here

Identical behaviour in prod. The markers did load, you could see an outline of where the image was supposed to be with a "file not found" symbol.

3. Add the leaflet images to the assets and reference them directly in the icon (also suggested in the ngx-leaflet Readme)

//leaflet-map.component.ts
import { Marker } from 'leaflet';
import * as L from "node_modules/leaflet";

...
  //My function to create markers on my map
  createDefaultMarker(mapMarker: MapMarker): Marker{
    return L.marker([mapMarker.latitude, mapMarker.longitude], {
      iconSize: [ 25, 41 ],
      iconAnchor: [ 13, 41 ],
      iconUrl: 'assets/marker-icon.png',
      shadowUrl: 'assets/marker-shadow.png'
    })
//angular.json
...
            "assets": [
              "src/favicon.ico",
              "src/assets",
              { "glob": "**/*", "input": "node_modules/tinymce", "output": "/tinymce/" },
              { "glob": "**/*", "input": "node_modules/leaflet/dist/images", "output": "assets/"}
            ],
...

On ng serve: Markers themselves are visible. Fails at loading marker-shadow.png from the wrong url and without the necessary hashes in the name: enter image description here

On ng build --prod: Has the nonsense URLs for marker_icon and marker_shadow: enter image description here

Questioner
Philipp Doerner
Viewed
0
Philipp Doerner 2020-12-02 01:17:33

This is neither a clean solution, nor one that actually fixes the problem, it just circumvents it.

In the end, what you're trying to do is load these 2 image files. Those should come from the HTTP server that is distributing your frontend but that is pretty precisely what leaflet is breaking. Nothing is stopping you from loading these 2 image files from a different place, like the HTTP Server that serves your backend.

Thus, I have uploaded the 2 image files to my backend server and then replaced the defined DefaultIcon of Leaflet's Marker prototype with one whose URL points to my backend server. That fixed it for me.

let DefaultIcon = L.icon({
  iconUrl: `${Constants.wikiMediaUrl}/leaflet/marker-icon.png`,
  shadowUrl: `${Constants.wikiMediaUrl}/leaflet/marker-shadow.png`,
  iconSize: [24,36],
  iconAnchor: [12,36]
});

L.Marker.prototype.options.icon = DefaultIcon;