Use Maptoolkit Maps in Angular
A map in Angular is a standalone component with a viewChild for the container, the map created
in ngAfterViewInit and removed in ngOnDestroy. The Angular-specific issue is the build
budget: the map library and Angular together are larger than the default limit for the initial
bundle.
The component
import { Component, ElementRef, AfterViewInit, OnDestroy, viewChild } from "@angular/core";
import * as maplibregl from "maplibre-gl";
@Component({
selector: "app-maptoolkit-map",
template: `<div #container class="map"></div>`,
styles: `.map { width: 100%; height: 400px; }`,
})
export class MaptoolkitMap implements AfterViewInit, OnDestroy {
private container = viewChild.required<ElementRef<HTMLDivElement>>("container");
private map?: maplibregl.Map;
ngAfterViewInit() {
this.map = new maplibregl.Map({
container: this.container().nativeElement,
attributionControl: { compact: false },
style: "https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=YOUR_API_KEY",
center: [11.4041, 47.2692],
zoom: 12,
});
}
ngOnDestroy() {
this.map?.remove();
}
}- The map is created in
ngAfterViewInit, the first hook in which the container element exists. remove()inngOnDestroyfrees the WebGL context. Browsers allow only a handful of live contexts, and a map that is never removed keeps its context after the route changes.- The container needs a height, or the map initializes into a zero-pixel element with no error.
The stylesheet
Add the MapLibre GL JS stylesheet to the styles array of the build target in angular.json, so it
is loaded once for the whole app:
"styles": [
"node_modules/maplibre-gl/dist/maplibre-gl.css",
"src/styles.css"
]Keep the map out of the initial bundle
A new Angular project fails its production build as soon as a component imports MapLibre GL JS
directly, with bundle initial exceeded maximum budget. MapLibre GL JS is about 0.9 MB minified
(1.1 MB for version 6), and together with Angular itself that is over the default 1 MB limit for
the initial bundle. Wrap the map in a @defer block where you use it:
@defer {
<app-maptoolkit-map />
} @placeholder {
<div style="height: 400px"></div>
}Angular then builds the map component into its own chunk and loads it after the page. The placeholder keeps the page from jumping when the map appears; give it the map’s height.
Raising the budget in angular.json also makes the build pass, but then the map library has to
download before the page first renders.
Next steps
- With a wrapper library, use ngx-leaflet for raster tiles or ngx-maplibre-gl for MapLibre GL JS.
- MapLibre GL JS examples for what to do with the map once it is mounted; the code
goes into
ngAfterViewInitafter the map is created.