Aurelia 2 Leaflet Maps (afterAttach problem)

Hi all,

Enjoying exploring Aurelia 2. When porting an app from 1 to 2 (this app) - ran into early difficulties attempting to initialise at third party component (leaflet maps). Here is a minimal Aurelia 1 example that works fine:

In the above, successfully creating a map component associated with a div element on the view in the attached() lifecycle look like this:

  map: LeafletMap;

  attached() {
    this.map = new LeafletMap(this.mapDescriptor);
  }

This is a minimal Au 2 version of the same application:

The equivalent initialisation

  map: LeafletMap;

  afterAttach() {
    this.map = new LeafletMap(this.mapDescriptor);
  }

However, the above yields exceptions:

leaflet-src.js:4115 Uncaught Error: Map container not found.
    at NewClass._initContainer (leaflet-src.js:4115)
    at NewClass.initialize (leaflet-src.js:3149)
    at new NewClass (leaflet-src.js:301)
    at Object.createMap [as map] (leaflet-src.js:4740)
    at new LeafletMap (leaflet-map.ts:18)
    at MyApp.afterAttach (my-app.ts:23)
    at Controller.afterAttach (controller.js:317)
    at AttachedQueue.process (lifecycle.js:231)
    at AttachedQueue.end (lifecycle.js:190)
    at Controller.attachCustomElement (controller.js:580)

I have tried all of the other lifecycle methods to no effect.

The element in the view is defined like this in both applications:

<div id="${mapDescriptor.id}" style="height:${mapDescriptor.height}px"></div>

I also tried various combinations of a hard coded id in the view to no effect. It seems that the element is not available on the DOM when the leaflet component attempts to create itself?

Any help appreciated - and I understand if this is still too early in Au2 to be a valid application.

Regards
Eamonn

Shouldn’t this:

Be:

afterAttach() {
    this.map = new LeafletMap(this.mapDescriptor.id); //<- id added
  }

Thanks for the suggestion - but the constructor is expecting a LeafletMapDescriptor -

from which the Id is extracted. I am using a wrapper around the leaflet objects as in the above source.

Regards
Eamon

Ah I see now your using a different version, I just use the leafletjs map directly, well, not directly I created a custom component to use it.

There I just use:

<template>
<require from="leaflet/dist/leaflet.css"></require>
<div id="mapid" style="height: 100%"></div>
</template>

and in attached()

    attached()
    {
        this.map = L.map('mapid').setView([0, 0], 2);
    }

I think I am doing more or less the same as you in my leaflet wrapper - but your example might be Aurelia 1 I think? Have you tried Leaflet in Aurelia 2 (using afterAttach instead)? This is where I am having trouble.
Thanks

I just created a dumber gist from my custom element and it seems to init just fine.
The map doesn’t show because I didn’t add in the tile server stuff.

https://gist.dumber.app/?gist=30d5dff05eab14b6f5c732a3454d96e1

Thanks!
They issue may be in my wrapper - back to the drawing board for the moment.
Thanks for your time
Eamonn

Just tried the au2 demo. The map is showing up fine without error in chrome and brave. I extended it a bit to add markers and everything seem to work.

Did not know about ArcGIS Online service for satellite view. Very useful to know. Thanks.

Thanks @atpw25 - yes it now works! I think I had errors in my leaflet wrapper.
Thanks for the ArcGIS recommendation.

Hi all,

thanks for comments and help to date.

While a leaflet map from the ‘main’ page is successfully created - I am not having much luck replicating same in a ‘component’ - particularly a component triggered by the new au2 router. This is illustrated here:

The map displays fine in the my-app component. However, the identical code in routes/home fails.

This is currently commented out in routes/home.ts:

export class Home implements IViewModel {
  ...
  map: LeafletMap;

  afterAttach() {
    //this.map = new LeafletMap(this.mapDescriptor);
  }

But when included, container not found exception thrown from leaflet

Uncaught (in promise) Error: Map container not found.
    at NewClass._initContainer (entry-bundle.js:39606)
    at NewClass.initialize (entry-bundle.js:38640)
    ...

Perhaps a lifecycle issue ? Not sure.

Any help/observations appreciated.

Eamonn

It appears to me that the document object leaflet is using is the global document before Aurelia 2 does its magic of composing.

When I trace through the Leaflet initialization to the point where it is doing a document.getElementById() everything is correct up to that point (i.e. the map id passed is available and correct). However, the document is just the raw index.html as viewed under document.body.innerHtml.

Thanks - and this is in “afterAttach”, where the DOM should be ready for this type of component?

Other attempts to update the Dom (via values in the view model) seem to work ok.

Perhaps too early AU2 for third party components in the views loaded by the new router?

I tried to get it to fail in dumber calling document.getElementId directly, and wasn’t able to get it to fail…
https://gist.dumber.app/?gist=b05534a798a3b2e865f0540fce04d35f

The same with a local project (not using Leaflet…

However, when I tried with a simplified leaflet app it failed. Still playing with it.

So my local app with routing is working now. (not exactly sure what I did, or if I did anything)

Here is the simplified leaflet element I used.

leaflet-map.html

<import from="leaflet/dist/leaflet.css"></import>
<div id="mapid" style="height: 400px; width: 400px;"></div>

leaflet-map.ts

import * as L from 'leaflet';

export class LeafletMap
{
    map: L.Map;

    constructor() { console.log("create LeafletMap"); }

    afterAttach()
    {
            let el = document.getElementById("mapid");
            console.log("LeaftletMap", el);

            this.map = L.map('mapid').setView([ 0, 0 ], 2);

            L.tileLayer(
                "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
                {
                    attribution:
                        "Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community",
                }
            ).addTo(this.map);
    }
}

routed to page content

<import from="leaflet-map"></import>

<h1>The about page.</h1>

<leaflet-map></leaflet-map>

This seems to only happen if router is involved. I think it may be related to the issue below.

Hi again,

I have reconfigured my demo to implement your leaflet-map element precisely (discarding my wrapper):

Still getting:

create LeafletMap
leaflet-map.ts:19 LeaftletMap null
leaflet-src.js:4115 Uncaught (in promise) Error: Map container not found.
    at NewClass._initContainer (leaflet-src.js:4115)
   ...
   at Controller.attach (controlle

I have even tried a delay as suggested in

The delay doesn’t seem to have an effect.

Quite puzzling - perhaps we need to ‘await’ the bug above to be resolved?

Thanks again for your efforts
Eamonn

Here is a repo with the sample I have working locally here.

From my brief tests using either versions (with or without leaflet wrapper), adding the await statement in afterAttach() allow the map to render.

async afterAttach() {
    await new Promise(resolve => setTimeout(resolve));
    let el = document.getElementById("mapid");
    console.log("LeaftletMap", el);
    ... 
 }

Although, it does caused another error, Map container is already initialized.. But that is a different issue.

Thanks!
Most helpful - and I can confirm it works as expected.

Hi all,

thanks for your help on getting Aurelia 2 up and running. The app is more or less complete now:

Source: https://github.com/edeleastar/oileain-2

The Aurelia 1 version is here:

Source : https://github.com/edeleastar/oileain

It was an interesting experience - apart from the afterAttach issue, the port was straightforward, with a modest code reduction overall.

Thanks again
Eamonn

3 Likes