Wednesday, February 26, 2014

Ember, Leaflet and Leaflet.Draw

I spent some time trying to figure out how to hook all these components together and decided to document the process here in case someone else is struggling with this as well.

I had a minimalistic Ember project in which I wanted to use Leaflet maps. I quickly found out about the ember-leaflet project. I added the ember-leaflet.js dependency to my index.html and following code into my ember application:

App.TileLayer = EmberLeaflet.TileLayer.extend({
    tileUrl: 'http://localhost:7000/tiles/{z}/{x}/{y}.png'
});
App.MapView = EmberLeaflet.MapView.extend({
    childLayers: [App.TileLayer],
    center: [34.227, -118.55],
    zoom: 11
});
Only problem I had with this, was that the leaflet map was taking too much space. Basically it filled the whole ember view. So, when I had other stuff in my handlebars template (besides the  <div id="map"></div> placeholder), the map would fill the whole template pouring over the other elements that I had in my template. This is why I ended up creating another ember view inside the outer view that would only contain the map. Then in the parent template I would have all my html elements as before but where I previously had the div-placeholder for the map, I put {{render "map"}} and then in map.hbs file just the map-div:  <div id="map"></div>.

So far so good.

I also needed draw controls and sure enough, Leaflet.Draw had everything I wanted. I added the css and js files into my project and added following options to my map configuration:

App.MapView = EmberLeaflet.MapView.extend({
   childLayers: [App.TileLayer],
   center: [34.227, -118.55],
   zoom: 11,
   options: {
       drawControl: true
   }
});
That would show the controls on the map just fine, but I wanted to get the edit controls working as well and as Leaflet.Draw documentation tells us I needed to create the Draw controls myself. I wasn't quite sure where to put that code so I ended up overriding the didCreateLayer and initialising the drawing controls there. But I still wasn't able to hook to all the events that Leaflet.Draw provides. Of course, I probably could have gotten the leaflet map handle from ember-leaflet and attach my event handler directly there, but that seemed like a hack. Going through the ember-leaflet code I noticed that it internally uses concatenatedProperties: ['events'] to gather all supported events. After that I just introduced that property in my derived view and added all Leaflet.Draw events there. At that point the code looked like this:

App.MapView = EmberLeaflet.MapView.extend({
    childLayers: [App.TileLayer],
    center: [34.227, -118.55],
    
    zoom: 11,

    events: [
        'draw:created',
        'draw:edited',
        'draw:deleted',
        'draw:drawstart',
        'draw:drawstop',
        'draw:editstart',
        'draw:editstop',
        'draw:deletestart',
        'draw:deletestop'
    ],

    didCreateLayer: function () {
        this._super();

        var map = this.get('layer'),
            drawnItems = new L.FeatureGroup(),
            drawControl;


        this.set('drawnItems', drawnItems);
        map.addLayer(this.drawnItems);
        drawControl = new L.Control.Draw({
            edit: {
                featureGroup: this.drawnItems
            }
        });
        map.addControl(drawControl);
    },

    "draw:created": function (e) {
        var layer = e.layer,
            type = e.layerType,
            drawnItems = this.get('drawnItems'),
            bounds;

        drawnItems.addLayer(layer);
        return this;
    }
});

That was it. Now I'm able to get all the Leaflet.Draw events to my view.



1 comment:

  1. Thanks for this post! If you want to create a stub for an EmberLeaflet.DrawLayer class, I'd be happy to link to it from the EmberLeaflet repo or even include it.

    ReplyDelete