How to integrate OpenLayers with Elm.Land

68 Views Asked by At

I'm using ElmLand's web component example as a guide to building a web component for OpenLayers having created a project using the site's introduction.

The app runs in localhost with a silent console window. But no OpenLayers map is appearing on the page. Just the earlier example "Say, hello" Button.

I've added a node to the Home_.elm view:

view : Model -> View Msg
view model =
    { title = "Homepage"
    , body =
        [ Html.button [ Html.Events.onClick UserClickedButton ]
            [ Html.text "Say, hello" ]
        , Html.node "map" [] []
        ]
    }

I've copied ol.css from node_modules/ol into ElmLand's static directory. My component is defined in ol-map.js:

import '../../static/ol.css'
import OSM from 'ol/source/OSM'
import TileLayer from 'ol/layer/Tile'
import { Map, View } from 'ol'
import { fromLonLat } from 'ol/proj'

window.customElements.define('ol-map', class extends HTMLElement {
    connectedCallback() {
        const map = new Map({
            target: 'ol-map', // also tried target: this
            layers: [
                new TileLayer({
                    source: new OSM(),
                }),
            ],
            view: new View({
                center: fromLonLat([0, 0]),
                zoom: 2,
            }),
        });
        setTimeout(function () {
            console.log("map initialising: ", map, map.isRendered())
        }, 5000)
    }
})

Main.css:

@import './ol.css';

body {
    padding: 32px;
    height: 100%;
}

#ol-map {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 100%;
}

And:

I've not edited interop.js since adding the previous example's OPEN_WINDOW_DIALOG code.

Browser Network pane reports status 200 for all the files downloaded including ol-map.js, ol.css, ol_source_OSM.js, ol_layer_Tile.js, ol.js, ol_proj.js and various names beginning with chunk-*.js.

I added a console.log (see code, above) and it does print to the console when I open the page at http://localhost:1234/

It's output of map{} includes:

loaded_ : true
maxTilesLoading_ : 16
ol_uid : "5"
values_ : {layergroup: LayerGroup, target: 'ol-map', view: View}

and isRendered() outputs false.

It all looks in order. But no map is displayed. I've tried integrating three.js as Elm Land's working with JS example and that worked fine.

2

There are 2 best solutions below

1
Jakub Hampl On

The string in Html.node needs to match the string in customElements.define for it to recognise the custom element.

You need to change Html.node "map" to Html.node "ol-map".

Also, AFAIK the target: 'map' line should really be target: this, as you are not looking up an element with the id="map", but rather the custom element you just created.

0
Carl On

One needs to create an element, attached to the custom element for the OpenLayers code to target:

import OSM from 'ol/source/OSM'
import TileLayer from 'ol/layer/Tile'
import { Map as MapElement, View } from 'ol'
import { fromLonLat } from 'ol/proj'


class OpenLayersElement extends HTMLElement {

    constructor() {
        super()
    }

    async connectedCallback() {
        // element required for OpenLayers to target
        const div = document.createElement('div')
        div.id = 'map'
        this.appendChild(div)

        // Create an OpenLayers map inside the custom element
        const map = new MapElement({
            target: 'map',
            layers: [
                new TileLayer({
                    source: new OSM(),
                }),
            ],
            view: new View({
                center: fromLonLat([0, 0]),
                zoom: 2,
            }),
        });
    }
}

window.customElements.define('ol-map', OpenLayersElement)