Den enkle guide til Angular Leaflet-kort
Leaflet er et af de vigtigste Javascript-baserede kortlægningsframeworks. Det bruges i vid udstrækning i hele branchen til at tilføje interaktive kort til websteder og applikationer.
Det kan imidlertid være vanskeligt at inkludere i Angular.io-apps. Det spiller ikke ligefrem pænt sammen med hverken Angular eller Typescript. Efter at jeg havde haft eventyret med at migrere et Leaflet-kortbaseret websted til Angular 8, stod det klart for mig, at der ikke fandtes nogen rigtig god reference til denne opgave. Disse indlæg er mit forsøg på at dokumentere det lærte. (UPDATE: Selv om denne artikel blev skrevet til Angular 8 – virker applikationerne og metoderne stadig i Angular 11).
Dette vil være et indlæg i flere dele :
- Grundlæggende (dette indlæg),
- Tilføjelse af kontrolelementer,
- Funktioner & Dynamiske kontrolelementer,
- Brugerdefinerede kontrolelementer, og
- Gå PWA – service workers med mere
Målet med denne serie er, at du vil være i stand til at oprette et Leaflet-kort i et Angular-CLI-baseret projekt uden besvær, besvær og blindgyder.
Det viser sig, at selv om det ikke er væsentligt svært at tilføje Leaflet til Angular.io, er det meget “fiddly”, da Leaflet ikke er særlig Angular- (eller Typescript-)venlig. Leaflet bruger en global L
variabelstruktur og .extend
metoder, der forvirrer TS. Den behandler også en mængde hændelser, der ville tvinge Angular-apps i knæ, hvis de blev sat gennem change detection.
Gluksomt nok er der et godt udgangspunkt i @asymmetrik/ngx-leaflet
. Det giver en temmelig meget boilerplate opsætning til grundlæggende kort. det kommer også med et par gode tutorials til grundlæggende kortlægning (her og her), men de synes at have mistet interessen, før de kom til de svære ting.
Kom i gang
Som i dokumentationen, skal du installere :
npm install leaflet
npm install @types/leaflet
npm install @asymmetrik/ngx-leaflet
og tilføje importen til app.module.ts
.
På dette tidspunkt – kan du stort set tilføje et simpelt kort til appen, så du tænker måske, hvad det hele gik ud på – men det kommer vi til.
CSS-komplikationer
Først skal vi tale om CSS. Ngx-leaflet kører leaflet uden for selve Angular – hvilket jeg forstår som den bedste måde at undgå, at Leaflet-hændelserne forårsager flere ændringshændelser i Angular.
Men, sandsynligvis på grund af dette, passer Leaflet ikke ind i Angular.ios CSS-indkapslingssystem, og alle CSS-referencer skal være på globalt niveau.
Det betyder normalt, at du tilføjer Leaflets CSS i angular.json på følgende måde
{
...
"styles": ,
...
}
eller i styles.css:
@import "./node_modules/leaflet/dist/leaflet.css"
Det gælder også for stylesheets for hver kontrol, hvilket bliver trættende, da hver kontrol i Leaflet har sin egen CSS. Jeg fandt ud af, at det bedste var at tilføje CSS’en for Leaflet til angular.json
og CSS’en for den enkelte kontrol til styles.css
primært for at opnå bedre læsbarhed.
Bemærk, at leaflet.css i begge tilfælde skal behandles før den øvrige CSS i styles.css. Ellers vil CSS’en for kontrolelementerne ikke være effektiv.
Compiler-komplikationer
Det er også her, vi begynder at støde på en af de første kompiler-komplikationer.
Leaflet-baserede programmer, der er oprettet på denne måde, fungerer for ng build
og fungerer med "aot": true
, men synes at fejle slemt for standardkonfigurationenng build --prod
.
Det ser ud til at være omkring optimering og jeg tror måske ned til tree-shaking. Efter nogle eksperimenter fandt jeg ud af, at ng build --prod
fungerer pålideligt, så længe "buildOptimizer": false
er i angular.json
.
Få ikke fanget i L
De fleste tutorials og dokumentation vil fortælle dig, at du skal bringe den globale L
-variabel ind i typescript ved hjælp af følgende import :
import * as L from 'leaflet'
Dette er sandt, hvis du vil bruge eksisterende JS-kode direkte i TS, men jeg fandt ud af, at så længe du har indlæst @types/leaflet
, kan du importere typedefs ved navn fra 'leaflet'
(e.f.eks. import {Map} from 'leaflet'
, og det hele fungerer som forventet. Det gør din TS-kode meget mere læsbar og, ja, TS-agtig!
Et simpelt kort
Så. Lad os oprette vores kort. I eksemplet nedenfor opretter jeg et OpenStreetMap-kort (da det er det, som min app arbejder på) som en separat komponent. På dette niveau kan det virke som ekstra arbejde, men vi skal se senere, at det giver os mulighed for at oprette en kortkomponent med ekstra funktioner, der kan genbruges flere steder i app-flowet.
Denne komponent har som standard et OSM-lag og en global visning i indstillingerne – selv om indstillingerne er opsat som @Inputs()
, så standardindstillingerne kan tilsidesættes af den overordnede komponent.
Kortkomponenten udsender også begivenheder, når kortet er klar, så andre dele af appen kan få adgang til kortet, og efter en zoombegivenhed med det nye zoomniveau. Du kan også udsende andre hændelser som f.eks. flytte- og laghændelser – men dette er det, som min app havde brug for, og jeg synes, at det demonstrerer princippet. Vi skal bruge kort- og zoombegivenhederne i de næste afsnit.
Kortet tilføjes til dom’et ved hjælp af ngx-leaflet-direktivet i komponentens HTML.
Endeligt bruges komponenten ved at tilføje følgende til den overordnede HTML:
<app-osm-map
="options"
(map$)="receiveMap($event)"
(zoom$)="receiveZoom($event)"
></app-osm-map>