Der einfache Leitfaden für Angular Leaflet Maps
Leaflet ist eines der wichtigsten Javascript-basierten Mapping-Frameworks. Es ist in der Industrie weit verbreitet, um interaktive Karten zu Websites und Anwendungen hinzuzufügen.
Es kann jedoch schwierig sein, es in Angular.io-Apps einzubinden. Es verträgt sich nicht gerade gut mit Angular oder Typescript. Nachdem ich das Abenteuer hatte, eine auf Leaflet basierende Website auf Angular 8 zu migrieren, war mir klar, dass es keine wirklich gute Referenz für diese Aufgabe gab. Diese Beiträge sind mein Versuch, das Gelernte zu dokumentieren. (UPDATE: Obwohl dieser Artikel für Angular 8 geschrieben wurde – die Anwendungen und Methoden funktionieren auch in Angular 11).
Dies wird ein mehrteiliger Beitrag sein:
- Die Grundlagen (dieser Beitrag),
- Hinzufügen von Steuerelementen,
- Funktionen & Dynamische Steuerelemente,
- Benutzerdefinierte Steuerelemente, und
- Going PWA – Service Worker und mehr
Das Ziel dieser Serie ist es, dass Sie in der Lage sind, eine Leaflet-Map in einem Angular-CLI-basierten Projekt zu erstellen, ohne Ärger, Aufregung und Sackgassen.
Es stellt sich heraus, dass das Hinzufügen von Leaflet zu Angular.io zwar nicht sonderlich schwer ist, aber sehr „fummelig“, da Leaflet nicht besonders Angular- (oder Typescript-) freundlich ist. Leaflet verwendet eine globale L
-Variablenstruktur und .extend
Methoden, die TS verwirren. Außerdem verarbeitet es eine Menge von Ereignissen, die Angular-Apps in die Knie zwingen würden, wenn sie durch die Änderungserkennung laufen würden.
Glücklicherweise gibt es einen guten Startpunkt in @asymmetrik/ngx-leaflet
. Es gibt auch ein paar gute Tutorials zu grundlegendem Mapping (hier und hier), aber sie scheinen das Interesse verloren zu haben, bevor sie zu den harten Sachen kamen.
Getting Started
Wie in der Dokumentation beschrieben, müssen Sie installieren:
npm install leaflet
npm install @types/leaflet
npm install @asymmetrik/ngx-leaflet
und die Importe zur app.module.ts
hinzufügen.
Zu diesem Zeitpunkt kann man eine einfache Karte in die App einfügen, so dass man denken könnte, worum es geht – aber dazu kommen wir noch.
CSS Komplikationen
Zuerst müssen wir über CSS sprechen. Ngx-leaflet führt Leaflet außerhalb von Angular selbst aus – was meines Erachtens der beste Weg ist, um zu vermeiden, dass die Leaflet-Ereignisse mehrere Änderungsereignisse in Angular verursachen.
Doch, wahrscheinlich deswegen, passt Leaflet nicht in das Angular.io-CSS-Kapselungssystem und alle CSS-Referenzen müssen auf der globalen Ebene liegen.
Das bedeutet normalerweise, dass man das Leaflet-CSS in angular.json wie folgt hinzufügt
{
...
"styles": ,
...
}
oder in styles.css:
@import "./node_modules/leaflet/dist/leaflet.css"
Das gilt auch für die Stylesheets für jedes Steuerelement, was mühsam wird, da in Leaflet jedes Steuerelement sein eigenes CSS hat. Ich habe herausgefunden, dass es am besten ist, das Leaflet-CSS in angular.json
und das CSS für die einzelnen Steuerelemente in styles.css
einzufügen, vor allem wegen der besseren Lesbarkeit.
Beachten Sie, dass in beiden Fällen das leaflet.css vor den anderen CSS in styles.css verarbeitet werden sollte. Andernfalls ist das CSS für die Steuerelemente nicht wirksam.
Compiler-Komplikationen
Hier stoßen wir auch auf eine der ersten Compiler-Komplikationen.
Leaflet-basierte Anwendungen, die auf diese Weise erstellt wurden, funktionieren für ng build
und funktionieren mit "aot": true
, scheinen aber bei der Standardng build --prod
-Konfiguration zu versagen.
Das scheint mit der Optimierung zu tun zu haben und ich denke, dass es vielleicht am Tree-Shaking liegt. Nach einigen Experimenten habe ich herausgefunden, dass ng build --prod
zuverlässig funktioniert, solange "buildOptimizer": false
in angular.json
ist.
Don’t Get Caught in L
Die meisten Tutorials und Dokumentationen sagen einem, dass man die globale L
-Variable mit folgendem Import in Typescript bringen muss:
import * as L from 'leaflet'
Das stimmt, wenn man bestehenden JS-Code direkt im TS verwenden will, aber ich fand heraus, dass man, solange man @types/leaflet
geladen hat, die Typedefs namentlich aus 'leaflet'
importieren kann (z.z. B. import {Map} from 'leaflet'
) und alles funktioniert wie erwartet. Das macht deinen TS-Code viel lesbarer und, nun ja, TS-isch!
Eine einfache Map
So. Lasst uns unsere Map erstellen. Im folgenden Beispiel erstelle ich eine OpenStreetMap-Karte (denn damit arbeitet meine Anwendung) als separate Komponente. Auf dieser Ebene mag dies als zusätzliche Arbeit erscheinen, aber wir werden später sehen, dass es uns ermöglicht, eine Kartenkomponente mit zusätzlichen Funktionen zu erstellen, die an mehreren Stellen im App-Flow wiederverwendet werden können.
Diese Komponente gibt eine OSM-Ebene und eine globale Ansicht in den Optionen vor – obwohl die Optionen als @Inputs()
eingerichtet sind, so dass die Voreinstellungen von der übergeordneten Komponente überschrieben werden können.
Die Kartenkomponente gibt auch Ereignisse aus, wenn die Karte bereit ist, so dass andere Teile der App auf die Karte zugreifen können, und nach einem Zoom-Ereignis mit der neuen Zoomstufe. Sie können auch andere Ereignisse ausgeben, z. B. Verschiebe- und Ebenenereignisse – aber das war es, was meine Anwendung brauchte, und ich denke, es demonstriert das Prinzip. Wir werden die Karten- und Zoom-Ereignisse in den nächsten Abschnitten verwenden.
Die Karte wird dem Dom mithilfe der ngx-leaflet-Direktive im Komponenten-HTML hinzugefügt.
Schließlich wird die Komponente verwendet, indem dem übergeordneten HTML Folgendes hinzugefügt wird:
<app-osm-map
="options"
(map$)="receiveMap($event)"
(zoom$)="receiveZoom($event)"
></app-osm-map>