Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixing Google Maps custom overlays with Backbone Views

TL;DR

Is PinView.prototype = _.extend(PinView.prototype, google.maps.OverlayView.prototype) the "proper" way to have a Backbone View inherit from another "class"?

Long read

We're redoing our site using Backbone and are working on including some mapping functionality.

I've got a Backbone view that handles placing <div>s onto specific points within the browser window; this seems like a natural thing to extend in order have Google's Map API place them on geographical coordinates.

According to the Google API, in order to generate a custom overlay you create a new object and set the prototype for that object to a new instance of google.maps.OverlayView. You then implement three functions on top of that object so that the object responds to:

onAdd

draw

onRemove

Where onAdd is responsible for generating the HTML and then applying it on top of the Map. This subsequently calls draw which positions the element correctly according to the LatLng pairs and bounds you've provided. onRemove gets called when you want to get rid of your layer.

So I've modified my View to include these three methods (which just call render and unrender and are bound to my collection). And then to make "the magic happen" I'm doing:

PinView.prototype = _.extend(PinView.prototype, google.maps.OverlayView.prototype)

Does this look right? I can post the code for the View and the Model on which it's based, but honestly, they're irrelevant to this example -- the code works and I'm able to place custom divs generated through Backbone model, view and controller components on the map without a issue, what I'm asking I guess (and maybe this question is more apropos for programmers.se, so let me know and I'll move it).

This seems to be the easiest way to make my PinView both a Backbone View and a Google Maps OverlayView, but I'm not 100% comfortable with prototypal inheritance to know if I'm doing something "wrong" or breaking something somewhere down the road.

like image 352
tkone Avatar asked Feb 01 '12 20:02

tkone


2 Answers

Nice idea! I'm usually a bit sceptical about weather or not you're 'correct' when things work so if you haven't run into a showstopper and the overlays shows up and does what the're supposed to do I'd say you are.

One thing to check out closer, though:

This isn't (and can't) be "real" multiple inheritance - that concept isn't really relevant in a prototype based language: one implementation of a method will inevitable "win" and overwrite the other implementation, at least when using _.extend()

This means that if there are members or methods with the same names in Backbone.View and google.maps.OverlayView the one last in your _.extend() call will be the one that takes over. But when I inspect them using Chrome's Developer Tools I didn't see any obvious collision of this kind.

So my recommendation: continue using this, just test a lot. I'd love to see an example of this technique some time.

like image 80
Jacob Oscarson Avatar answered Sep 20 '22 07:09

Jacob Oscarson


Ah! So I've been doing the above, but it's never felt right.

Then I found this discussion on a Backbone group which leads me to the following:

var MyView = (function(){
    var view = function(){
        Backbone.View.apply(this, arguments);
    };

    view.extend = Backbone.View.extend;

    _.extend(view.prototype, Backbone.View.prototype, google.maps.OverlayView.prototype, [other prototypes...], { [VIEW DEFINITION] });

    return view;
}());

This way if we need to override any of the definitions in a class we're extending from, we can since it's earlier in the _.extend chain (later definitions overwrite earlier definitions).

I'm working on 'extending' extend to keep track of the "parent" object's references that would be overridden and providing a method to call them still (like Python's super call). I haven't decided if this should be done through monkey-patching, an intercepter pattern (via underscore's _.tap() method or something else, but I think it'll add a lot of flexibility.

This would allow you to define an initialize view in your "parent" class which could be called by doing something like _.super('ParentClass', 'initialize'); at the end of the "child" class's initialize routine...

like image 39
tkone Avatar answered Sep 21 '22 07:09

tkone