A few weeks ago, I started playing around with Backbone.js. At the end of last week, I noticed something on Twitter about Leaflet. I’ve been a long-time devotee of the Play! framework, as you may be aware if you’ve ever read this blog before.
Play 2 was released yesterday. This seems like a perfect time to mix them all together, just for fun*
I originally looked at combining Backbone and Leaflet to create markers on a map, which was a fun and easy 20 minutes – it was pure client-side code, simple to do and unbelievably pointless. Unless the map markers could be shared across multiple users, it was ultimately useless. That’s when I decided to hijack and re-purpose Play 2’s websocket-chatroom sample application- I thought it would be nice to have the places you mark on your map pop up in real-time on the map that other people are looking at, and the chatroom code required minimal tweaking to do this.
At this point, I should apologise. I just got back from the gym, and I’m too tired to think coherently enough to write good blog prose. Luckily, we’re about to head into the technical details
On start-up, mapcrap acts like a tradional MVC app – it gets the existing map places from the database via a RESTful call, and displays them on the map. From this point on, everything is then driven by websocket interactions. When you create a place, instead of it being persisted via REST, the JSON of the place is sent over the websocket. The server-side app then persists the place and broadcasts it to every user’s websocket using Akka.
in.onMessage(new F.Callback() { public void invoke(JsonNode event) { Place place = Json.fromJson(event, Place.class); place.createdOn = new Date(); place.save(); defaultMap.tell(new MapEvent(userId, place)); } });
The locally-added place isn’t added to the local collection of places directly – instead, it receives it through the broadcast. This is a lazy way of getting around Backbone collections not accepting duplicate models.
When the client receives a push message informing it that a new place has been added, it adds the place to the Backbone collection of places – in turn, the event listener on the collection triggers the addition of a popup to the map.
this.model.bind("add", function(place){ var view =new PopupContentView({model:place}); var latlng = new L.LatLng(place.get('latitude'), place.get('longitude')); var messagePopup = new L.Popup({autoPan:false, closeButton:false}); messagePopup.setLatLng(latlng); messagePopup.setContent(view.render().el); self.options["map"].addLayer(messagePopup); }, this);
Notes:
- The easiest way to see this working is to open two browsers and point them both at http://localhost:9000
- Left-click on the map to open a “create place” popup. If you’ve entered an avatar URL in the Avatar URL text box, this image will also be displayed in the pop-up
- Enter a message, and click Create! – the pop-up will (or should, at least) appear on your map and the map in the other browser window
- This doesn’t work in Internet Explorer, because it doesn’t support web sockets
- If you want to try this on a network, change the URL in models.js from localhost to a hostname/IP address
- This isn’t on Heroku because, the last I heard at, web sockets are not supported there
You can download the mapcrap – you’ll need Play 2.0 to run it.
Instructions:
- Unzip it
- cd into the directory
- play
- run
- Point your browsers at http://localhost:9000
If you have any problems, please let me know!
* My parents are staying with me this week. My dad asked, “what is this good for?” about 2 hours ago. The explanation of the joy of coding and the satisfaction of writing something vaguely cool is still, sporadically, going on.
as usual, great stuff coming from this blog!
Last week I just posted a similar article (Chat application with Play1+KnockoutJS) Besides Backbone.js, you should definitely take a look at KnockoutJS as well.
Volkan, very nice work! I’ll have a proper look at it later.
Very nice article, just what I was looking for.