The problem
I'm working on a leaflet view for emberjs, and I'm having some problems. Leaflet is an external library and somewhat irrelevant to the question, but just know that it is a mapping library.
Consider a simple property like zoom level. Leaflet map instances have a zoom level accessible through map.getZoom()
and assignable through map.setZoom(zoomLevel)
. Also, users can interact with the map, and change its zoom level. Leaflet allows us to register a callback when the zoom changes.
I would like my "Ember-Leaflet" View to have a zoomLevel
ember property. This way I can benefit from the ember object model (bind zoomLevel
to a template or to another value, for example), and we're doing it "the ember way".
Base solution
What I currently have is a subclass of Ember.View, with a zoomLevel property. On didInsertElement
I create the Leaflet map instance. The code is commented and should be self-explanatory.
App.Leaflet = Ember.View.extend({
classNames : ['ember-leaflet'],
//default zoom level
zoomLevel : 13,
didInsertElement : function() {
var self = this;
var zoomLevel = this.get('zoomLevel');
// create map instance
var map = L.map(this.$().get(0)).setView(center, zoomLevel);
// configure map instance...
// Event listeners
map.on('zoomend', function(e) {
self.set('zoomLevel', e.target.getZoom());
});
// save map instance
this.set('map', map);
}
});
Checklist
To make this question more "answerable", I think that a solution to this problem should fulfill the following requirements:
- When the property
zoomLevel
is changed, the map should change its zoom level accordingly (usingmap.setZoom(zoomLevel)
) - When the user interactively changes the zoom on the map, the
zoomLevel
property should be changed (probably using leaflet map'szoomend
event callback)
Notice that we have here a kind of "circular dependency", i.e "Do something (setZoom on map) when zoomLevel
changes" and "when something happens (user changes zoom), change zoomLevel
". I would like a solution that could avoid this circular observer dependencies. Ember's notifyPropertyChange could be a solution.
This seemed like an ideal task for Ember's computed properties, but I didn't know what to put in the dependent properties string. zoomLevel
is basically a property that is dependent on something that isn't an ember property.
Update
My first attempt was to use an observer to set the zoom on leaflet and to bind an event on leaflet zoomend
to set the zoomLevel
on my view.
Problem: When I set zoomLevel on the zoomend leaflet event, I'm automatically triggering again my observer. Did I make myself clear?
So, this sequence of events happen:
- change
zoomLevel
on interactive map - leaflet fires
zoomend
event zoomend
event callback sets emberzoomLevel
- observer runs
- unecessarily calls
setZoom
on leaflet
This is not efficient, but I wouldn't mind if it worked. The problem is that when the user changes zoom multiple times very quickly (a simple long scroll on map) the observer is called multiple times, confusing leaflet.