La Guía Simple de Mapas Leaflet en Angular

Leaflet es uno de los principales frameworks de mapas basados en Javascript. Es ampliamente utilizado en la industria para añadir mapas interactivos a sitios y aplicaciones web.

Sin embargo, puede ser difícil de incluir en las aplicaciones de Angular.io. No se lleva precisamente bien con Angular ni con Typescript. Después de tener la aventura de migrar un sitio basado en mapas de Leaflet a Angular 8 me quedó claro que no había ninguna referencia realmente buena para esta tarea. Estos posts son mi intento de documentar los aprendizajes. (ACTUALIZACIÓN: Aunque este artículo fue escrito para Angular 8 – las aplicaciones y métodos siguen funcionando en Angular 11).

Este va a ser un post de varias partes :

  • Los fundamentos (este post),
  • Añadir controles,
  • Funciones & Controles dinámicos,
  • Controles personalizados, y
  • Volviéndose PWA – service workers y más

El objetivo de esta serie es que seas capaz de crear un mapa Leaflet en un proyecto basado en Angular-CLI sin complicaciones, alborotos y callejones sin salida.

Resulta que, aunque añadir Leaflet a Angular.io no es significativamente difícil, sí es muy «engorroso» ya que Leaflet no es particularmente amigable con Angular (o Typescript). Leaflet utiliza una estructura global de variables L y métodos .extend que confunden a TS. También procesa un volumen de eventos que pondría de rodillas a las aplicaciones de Angular si se sometieran a la detección de cambios.

Por suerte, hay un buen punto de partida en @asymmetrik/ngx-leaflet. Esto proporciona una configuración más o menos boilerplate para los mapas básicos. también viene con un par de buenos tutoriales para el mapeo básico (aquí y aquí), pero parecen haber perdido el interés antes de llegar a las cosas difíciles.

Cómo empezar

Según la documentación, es necesario instalar :

npm install leaflet
npm install @types/leaflet
npm install @asymmetrik/ngx-leaflet

y añadir las importaciones a la app.module.ts.

En este punto – usted puede más o menos añadir un simple mapa a la aplicación, por lo que podría pensar lo que el alboroto era – pero estamos llegando a eso.

CSS Complicaciones

En primer lugar, tenemos que hablar de CSS. Ngx-leaflet ejecuta leaflet fuera del propio Angular – lo que entiendo que es la mejor manera de evitar que los eventos de Leaflet provoquen múltiples eventos de cambio en Angular.

Sin embargo, probablemente debido a esto, Leaflet no encaja en el sistema de encapsulación de CSS de Angular.io y todas las referencias CSS tienen que estar a nivel global.

Esto suele significar que añades el CSS de Leaflet en angular.json de la siguiente manera

{
...
"styles": ,
...
}

o en styles.css:

@import "./node_modules/leaflet/dist/leaflet.css"

Esto también es válido para las hojas de estilo de cada control, lo que se hace cansino ya que, en Leaflet, cada control tiene su propio CSS. Encontré que lo mejor era añadir el CSS de Leaflet a angular.json y el CSS de cada control a styles.css principalmente para una mejor legibilidad.

Nota que, en cualquier caso, el leaflet.css debe ser procesado antes que el otro CSS en styles.css. De lo contrario, el CSS para los controles no será efectivo.

Complicaciones del compilador

Aquí es también donde empezamos a encontrarnos con una de las primeras complicaciones del compilador.

Las aplicaciones basadas en leaflets creadas de esta manera funcionan para ng build y funcionan con "aot": true pero parecen fallar mucho para la configuración por defectong build --prod.

Esto parece estar en torno a la optimización y creo que tal vez se debe al tree-shaking. Después de algunos experimentos, he encontrado que ng build --prod funciona de forma fiable siempre que "buildOptimizer": false esté en angular.json.

No te quedes atrapado en L

La mayoría de los tutoriales y la documentación te dirán que necesitas traer la variable global L a typescript usando el siguiente import :

import * as L from 'leaflet'

Esto es cierto si quieres usar código JS existente directamente en el TS, pero encontré que mientras cargues @types/leaflet puedes importar los typedefs por nombre de 'leaflet' (e.g. import {Map} from 'leaflet' y todo funciona como se espera. Esto hace que tu código de TS sea mucho más legible y, bueno, ¡de TS!

Un mapa simple

Entonces. Vamos a crear nuestro mapa. En el ejemplo de abajo, estoy creando un mapa de OpenStreetMap (ya que es en lo que trabaja mi aplicación) como un componente separado. A este nivel, esto puede parecer un trabajo extra pero ya veremos más adelante, nos permite configurar un componente de mapa con características adicionales que pueden ser reutilizadas en múltiples lugares del flujo de la app.

Este componente emite por defecto una capa OSM y una vista global en las opciones – aunque las opciones se configuran como @Inputs() por lo que los valores predeterminados pueden ser anulados por el componente padre.

El componente de mapa también emite eventos cuando el mapa está listo para que otras partes de la app puedan acceder al mapa, y después de un evento de zoom con el nuevo nivel de zoom. También puede emitir otros eventos como eventos de movimiento y de capa – pero esto es lo que necesitaba mi app y creo que demuestra el principio. Utilizaremos los eventos de mapa y zoom en las siguientes secciones.

El mapa se añade al dom utilizando la directiva ngx-leaflet en el HTML del componente.

Por último, el componente se utiliza añadiendo lo siguiente al HTML padre:

<app-osm-map
="options"
(map$)="receiveMap($event)"
(zoom$)="receiveZoom($event)"
></app-osm-map>

Lee esto a continuación