batman.js views are one of the best ways to integrate other JS libraries with batman.js data structures like
Batman.Set. For example, you can use a custom view to display
Batman.Models with leaflet.js
I’ve always wanted to try batman.js + leaflet. I had to:
@optionto define view APIs
- Initialize the custom view, controlling for async loading of data & map
Batman.Objects to keep leaflet up-to-date.
- Listen to leaflet to keep batman.js up to date
I ended up making an abstract
LeafletView, implemented by
@option in Custom Views
@option allows you to pass values explicitly into your custom view. That way, you can eliminate the guesswork of climbing the view tree or looking up to the controller for some value.
It provides a view binding and an accessor for your custom view. In my case, I used:
class App.LeafletView extends Batman.View @option 'draggable'
To provide in my HTML:
<div data-view='App.LeafletView' data-view-draggable='true'></div>
And in my view code:
@get('draggable') # => returns the value passed to the binding
This also works for objects, as in
<div data-view='App.LeafletPointView' data-view-item='monument'></div>
Then I have easy access to my record:
Initializing Custom Views
Initializing custom batman.js views is tough because:
- Views are constructed before they’re added to the DOM
- Bindings are initialized without values (and their objects may not be loaded from the server yet)
- Lifecycle events may fire more than once
So, you have to be prepared for undefined values and for
viewDidAppear to be fired more than once.
observeOnceto fire on change from
undefinedto some value. My case was different because I had to wait for the binding and for the map to load, hence the
- Check for initialization in
Keeping Other Libraries up to Date
Batman.Object to the object itself.
For example, to update a leaflet marker when a
Batman.Object is changed, you have to observe the
Batman.Object so that whenever
longitude changes, you update the marker:
# From App.LeafletPointView, @get('item') returns the object @observe 'item.latitude', (nv, ov) -> @updateMarker(@get('item'), centerOnItem: true) if nv? @observe 'item.longitude', (nv, ov) -> @updateMarker(@get('item'), centerOnItem: true) if nv?
You have to link the other way too. To update a record when its marker is updated (by dragging), create a handler:
# from App.LeafletView marker.on 'dragend', => # ... # get values from leaflet and update batman.js latLng = marker.getLatLng() item.set 'latitude', latLng.lat item.set 'longitude', latLng.lng # ...