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".
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);
}
});
To make this question more "answerable", I think that a solution to this problem should fulfill the following requirements:
zoomLevel
is changed, the map should change its zoom level accordingly (using map.setZoom(zoomLevel)
)zoomLevel
property should be changed (probably using leaflet map's zoomend
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.
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:
zoomLevel
on interactive mapzoomend
eventzoomend
event callback sets ember zoomLevel
setZoom
on leafletThis 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.
Ok, I think I've did it.
I used a computed property and an auxiliary property.
App.Leaflet = Ember.View.extend({
classNames : ['ember-leaflet'],
//default zoom level
zoomLevelValue : 13,
zoomLevel : function(key, value){
// getter
if (arguments.length === 1) {
var zoomLevel = this.get('zoomLevelValue');
return zoomLevel;
// setter
} else{
var map = this.get('map');
this.set('zoomLevelValue', value);
map.setZoom(value);
return value;
}
}.property('zoomLevelValue'),
didInsertElement : function() {
var self = this;
var zoomLevel = this.get('zoomLevel');
var map = L.map(this.$().get(0)).setView(center, zoomLevel);
// configure map instance...
// Event listeners
map.on('zoomend', function(e) {
console.log('zoomend', 'Setting zoomLevel '+e.target.getZoom());
self.set('zoomLevelValue', e.target.getZoom());
});
// save map instance
this.set('map', map);
}
});
Basically, there are two scenarios:
However I have a new problem, but I think it is leaflet related. When I call setZoom
when another zoom animation on course, this setZoom is ignored.
For example, executing:
map.setZoom(1);
map.setZoom(12);
ends up in the 1
zoom level.
Maybe a leaflet related SO question can help.
To get from zoomLevel in ember to setZoom you can use an observer.
To go the other way you may have to bind an event to zoomChange on the leaflet
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With