Florestan Korp
Florestan Korp

Reputation: 712

Svelte / SvelteKit importing an npm library returns error when trying to work with Leaflet

I'm trying to learn Svelte / SvelteKit by porting over an existing Angular application. The app should show a Leaflet map with a heatmap layer as overlay. The map part works and is robust, even when I navigate or refresh Svelte handles it fine. The heatmap on the other hand only loads when the app initializes for the first time as you can see here:

enter image description here

However When I refresh I get this error and the whole Map.svelte component doesn't load at all anymore with the following error message in the console:

Uncaught (in promise) TypeError: Leaflet.heatLayer is not a function

enter image description here

I suspect it has to do with the way the lifecycle handles imports, because in my Angular app the imports don't have to be done in a life cycle method in order for them to work, whereas the only way to get Leaflet to even render in SvelteKit I have to do an async import.

Can anyone clarify what's going on with the Leaflet.heatlayer error and how I can fix it?

Map.svelte

<script lang="ts">
    import type { HeatLayer, Map } from 'leaflet';
    import { onMount } from 'svelte';

    let Leaflet;
    let map: Map;
    let heatLayer: HeatLayer;

    onMount(async () => {
        Leaflet = await import('leaflet');
        import('leaflet.heat');

        const heatLatLngTuple = await fetchData(); // fetchData returns data from JSON file

        const mapTiles = Leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
            attribution: '© OpenStreetMap'
        });

        heatLayer = Leaflet.heatLayer(heatLatLngTuple, {. // THIS LINE IS CAUSING THE ERROR
            radius: 20,
            blur: 25,
            minOpacity: 0,
            maxZoom: 6,
            max: 12
        });

        map = Leaflet.map('map', {
            center: [51.505, -0.09],
            zoom: 6,
            layers: [mapTiles, heatLayer]
        });
    });
</script>

<div id="map" />

Things I've tried:

ReferenceError: window is not defined

Resources:

Upvotes: 1

Views: 1433

Answers (2)

rgfx
rgfx

Reputation: 338

I had this same issue on refresh, you need import the module inside the on mount. It's not the same as your code. But you get that point.

    onMount(async () => {

        const leafletModule = await import('leaflet');
        L = leafletModule.default;

Upvotes: 1

Florestan Korp
Florestan Korp

Reputation: 712

As this answer points out there is an additional way of importing that I didn't know about using vite-plugin-iso-import

After getting that set up my component now works and after importing "leaflet.heat" with ?client added and moved to the top level of my imports. Here is the link to the FAQ with a detailed explanation.

After the changes my component now looks like this:

Map.svelte

<script lang="ts">
    import type { HeatLayer, Map } from 'leaflet';
    import { onMount } from 'svelte';
    import Leaflet from 'leaflet?client'; // provides definition of 'L' needed by Leaflet
    import 'leaflet.heat?client'; // Note the '?client' after the module name which makes sure 'leaflet.heat' always has access to the 'window' object

    let map: Map;
    let heatLayer: HeatLayer;

    onMount(async () => {
        const heatLatLngTuple = await fetchData(); 

        const mapTiles = Leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
            attribution: '© OpenStreetMap'
        });

        heatLayer = Leaflet.heatLayer(heatLatLngTuple, {. // THIS LINE IS CAUSING THE ERROR
            radius: 20,
            blur: 25,
            minOpacity: 0,
            maxZoom: 6,
            max: 12
        });

        map = Leaflet.map('map', {
            center: [51.505, -0.09],
            zoom: 6,
            layers: [mapTiles, heatLayer]
        });
    });
</script>

<div id="map" />

Upvotes: 1

Related Questions