Reputation: 175
I am currently using asymmetrik's ngx-leaflet and ngx-leaflet-markercluster with the following versions:
"@asymmetrik/ngx-leaflet": "^5.0.2",
"@asymmetrik/ngx-leaflet-markercluster": "^2.1.1"
During development everything is fine. However, when I build the angular project for production (ng serve --prod
), I am not able to see the cluster markers and I get the following error in Chrome's console:
ERROR TypeError: a.markerClusterGroup is not a function
at t.ngOnInit (main-es2015.94505399b2d83c23de95.js:1)
at main-es2015.94505399b2d83c23de95.js:1
at main-es2015.94505399b2d83c23de95.js:1
at Kb (main-es2015.94505399b2d83c23de95.js:1)
at xw (main-es2015.94505399b2d83c23de95.js:1)
at Object.updateDirectives (14-es2015.a3c30690716486447af1.js:1)
at Object.updateDirectives (main-es2015.94505399b2d83c23de95.js:1)
at Xb (main-es2015.94505399b2d83c23de95.js:1)
at rw (main-es2015.94505399b2d83c23de95.js:1)
at nw (main-es2015.94505399b2d83c23de95.js:1)
I am importing 'leaflet' and 'leaflet.markercluster' in my component. Below is the map.component.ts code:
import { Component, Input, SimpleChanges } from '@angular/core';
import { tileLayer, latLng, control, marker, divIcon, LatLngBounds, MarkerClusterGroup, MarkerClusterGroupOptions } from 'leaflet';
import { NgElement, WithProperties } from '@angular/elements';
import * as L from 'leaflet';
import 'leaflet.markercluster';
...
@Component({
selector: 'map',
styleUrls: ['./map.component.scss'],
templateUrl: './map.component.html'
})
export class MapComponent {
layers = [];
markerClusterGroup: L.MarkerClusterGroup;
markerClusterData: any[] = [];
maxBounds: L.LatLngBounds;
map: L.Map;
options = {
layers: [
tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18 }),
],
zoom: 2,
zoomControl: false,
maxZoom: 18
};
markerClusterOptions: L.MarkerClusterGroupOptions = {
showCoverageOnHover: true,
animate: true,
chunkedLoading: true,
zoomToBoundsOnClick: true,
spiderfyOnMaxZoom: true,
chunkDelay: 500,
animateAddingMarkers: true,
chunkInterval: 7,
iconCreateFunction: function (cluster) {
var markers = cluster.getAllChildMarkers();
var icon = divIcon({
iconSize: [35, 35],
iconAnchor: [10, 10],
popupAnchor: [10, 0],
shadowSize: [0, 0],
html: '<div>' + cluster.getChildCount() + '</div>'
});
if (markers.find(x => x.options.alt == enum.A)) {
icon.options.className = 'cluster cluster-critical';
} else if (markers.find(x => x.options.alt == enum.B)) {
icon.options.className = 'cluster cluster-warning';
} else {
icon.options.className = 'cluster cluster-normal';
}
return icon;
}
};
ngOnChanges(changes: SimpleChanges): void {
this.fixMap();
}
mapReady(map: L.Map) {
this.map = map;
map.addControl(control.zoom({ position: 'bottomright' }));
this.fixMap();
}
fixMap() {
if (this.map) {
this.setBounds();
this.map.fitBounds(this.maxBounds);
this.initMarkers();
setTimeout(() => {
this.map.invalidateSize();
}, 0);
}
}
markerClusterReady(group: L.MarkerClusterGroup) {
this.markerClusterGroup = group;
this.markerClusterGroup.addLayers(this.markerClusterData);
}
initMarkers() {
this.markerClusterData = [];
this.objectList.forEach(x => {
var location = latLng(x.location.latitude, x.location.longitude);
var system = marker(location, {
icon: divIcon({
iconSize: [30, 30],
iconAnchor: [10, 10],
popupAnchor: [10, 0],
shadowSize: [0, 0],
className: this.getMarkerStyle(x),
}),
title: x.serialNumber,
alt: x.objectStatusSummary.status
});
system.bindPopup(fl => this.createPopupComponent(x), {
className: 'popup',
closeButton: false
}).on('click', (data) => {
this.focusOnMarker(x);
});
this.markerClusterData.push(system);
});
}
focusOnMarker(object: Object) {
var bounds = new L.LatLngBounds([[object.objectLocation.latitude, object.objectLocation.longitude]]);
this.map.fitBounds(bounds, {
animate: true,
duration: 1,
easeLinearity: 1,
maxZoom: this.map.getZoom(),
noMoveStart: true
});
}
setBounds() {
var latLngArray: any[] = [];
if (this.objectList && this.objectList.length > 0) {
this.objectList.forEach(x => {
latLngArray.push({
lat: x.objectLocation.latitude,
lng: x.objectLocation.longitude,
});
});
}
else {
latLngArray.push({
lat: 0,
lng: 0
});
}
this.maxBounds = new L.LatLngBounds(latLngArray);
}
getMarkerStyle(object: object) {
return 'marker marker-' + this.businessHelper.getClassByStatus(<objectHealthStatusEnum>object.objectStatusSummary.status) + ' marker-' + object.serialNumber;
}
resetZoom() {
this.map.fitBounds(this.maxBounds, {
animate: true,
duration: 1,
easeLinearity: 1,
noMoveStart: true
});
}
public createPopupComponent(object) {
const popupEl: NgElement & WithProperties<MapPopupComponent> = document.createElement('popup-element') as any;
popupEl.addEventListener('closed', () => document.body.removeChild(popupEl));
popupEl.object = object;
document.body.appendChild(popupEl);
return popupEl;
}
}
This is the map.component.html code:
<div class="map-wrapper">
<div
leaflet
[leafletOptions]="options"
(leafletMapReady)="mapReady($event)"
[leafletMarkerCluster]="markerClusterData"
[leafletMarkerClusterOptions]="markerClusterOptions"
(leafletMarkerClusterReady)="markerClusterReady($event)"
></div>
</div>
You can find the main code here.
Any ideas?
Upvotes: 3
Views: 2796
Reputation: 61
I've come across this issue too. I solved this problem by next steps:
"scripts": [
"node_modules/leaflet/dist/leaflet.js",
"node_modules/leaflet.markercluster/dist/leaflet.markercluster.js"
];
These scripts will add “L” object as a field of the global “window” object. Now you can get Leaflet extended by “markercluster” plugin as: window.L anywhere;
Define a private field (“leaflet” in my case) in component/directive/services classes, where you use Leaflet:
import 'leaflet.markercluster'; // you need import this locally anyway for "markercluster" methods ts-declaration
@Component({})
export class AppComponent() {
private leaflet = window.L; // here is our leaflet with the clusters
initMap() {
const map = this.leaflet.map('map').setView([0, 0], 10);
const markers = this.leaflet.markerClusterGroup(); // now this method is defined!
map.addLayer(markers);
}
}
Works for me on Angular 18.2.0.
Upvotes: 0
Reputation: 21
Hello I had same issue in Angular project. I'm using NPMs.
npm i leaflet @types/leaflet
npm i leaflet.markercluster @types/leaflet.markercluster
npm i leaflet.fullscreen @types/leaflet.fullscreen
Solution: Import leaflet and its plugins in your app.module.ts
file:
import 'leaflet';
import 'leaflet.markercluster';
import 'leaflet.fullscreen'
And also import these like this in your component where you use your map:
import * as L from 'leaflet';
import 'leaflet.markercluster';
import 'leaflet.fullscreen';
It is because of Lazy loading modules. Project will work on localhost, but build version won't. So you have to import like I said above.
Upvotes: 2
Reputation: 405
I have had the same problem and found a solution. The problem is that there are different leaflets imported as 'L' from the definitions. Make sure to import leaflet first, then the other packages, e.g.:
import * as L from 'leaflet';
import { Map, MapOptions, MarkerClusterGroup, MarkerClusterGroupOptions } from 'leaflet';
import 'leaflet.markercluster';
another guess would be that the timing in production is different than on develop. your initMarkers
gets called via ngOnChanges
but that might be to early. The map or the cluster might not be initialized yet. Try to call initMarkers
within markerClusterReady
or mapReady
Upvotes: 2
Reputation: 17
Don't import leaflet through html, install the package and import the modules you need instead
Upvotes: 0