Mixing Google Maps custom overlays with Backbone Views - google-maps-api-3

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.

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.

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...

Related

Flex: Additional label in ButtonBarButton

I'm trying to extend ButtonBarButton to include an additional label that shows the number of updates for the respective box in a viewstack (which is the tabbar's dataProvider).
I can get the additional label (named indicatorLabel) to read an initial value from data, but I can't get it to update automatically like the actual "label" attribute. My understanding was that you could bind to the data object, but that doesn't appear to be the case.
The box that is used in the viewstack has an attribute called _indicator
[Bindable]
public var _indicator:String;
Which I know is updating properly because I can see it updating in the box (which also has a label bound to it). It appears to just not update the buttonbarbutton.
My buttonbarbutton class has the following (where init() is called in creationComplete
[SkinPart]
public var indicatorLabel:spark.components.Label;
private function init():void
{
indicatorLabel = data._indicator;
addEventListener("dataChange", onDataChangeHandler);
}
private function onDataChangeHandler(e:Event):void
{
trace(e.target.label + ' ' + e.target._indicator);
}
I'm guessing my assumptions for either databinding or the data obj that gets passed to the button are incorrect. Any help is appreciated.
I have answered a question here just a few days ago that is closely related to this one. It's a bit more abstract, but it should answer yours too. Have a look at Flex : Communicate between the skin and the data model?
As for your additional question about performance improvement: you just have to keep in mind that whenever you use binding, the compiler will generate some event listeners to listen for changes in the data. Of course that's not an issue with a one-off component or even with a bunch of instances, but it may become one if you have a List with thousands of components that all use binding. First of all the garbage collector won't clean these components up so easily when they're no longer needed and secondly - depending on your implementation - thousands of events may be firing at once.
bottom line: you could consider the commitProperties() approach performance tuning, hence you shouldn't consider it until you actually run into a performance issue.
on the other hand from an architectural point of view: that approach allows for a cleaner separation of the component and its skin. With the hostComponent / binding approach you could say the skin knows too much: it has to know about its host component and its properties. While the other one allows you to have a completely 'dumb' skin. So again I tend to use binding with one-off components and commitProperties() with highly reusable ones.
In the end it's all a trade-off (because the 'clean' way is more complex and more work) and it's up to you to make a weighed decision for each particular situation.

Communication between Flex module and Application

Ok, modules in Flex are popular but I have no idea why documentation and examples on the different uses of Flex modules can be so scarce.
Anyway, for this question, I will take the classic Employee/Department example. I have a main.mxml that contains an mx:TabNavigator. Each tab is loaded by an s:ModuleLoader.
Tables: Employees {empID,empName,deptID}, Deparments {deptID,deptName}
The Tab Navigator contains only one tab (for our example) called Employee. I have an Employee.mxml module. In that module, I have a datagrid that is populated with Employee details. I use the getEmployees($deptID) function. This function, as you may guess, returns me an array of Employees who work in a particular department.
Outside the TabNavigator, I have a departmentDropDownList that is populated with departments.deptName.
My objective is to load the Employee module when I select a particular department from the DropDownList. I have a changeHandler for the DropDownList that can give me the deptID.
protected function departmentDropDownList_changeHandler(event:IndexChangeEvent):void
{
MyDeptID=departmentDropDownList.selectedItem.deptID;
//var ichild:*=employeeModule.child as IModuleInfo;
}
Now, the million dollar question is: How do I pass this deptID to the Employees module. The latter has an employee_creationCompleteHandler that calls getEmployees(deptID):
protected function EmployeesDg_creationCompleteHandler(event:FlexEvent):void
// I only need to get the deptID from the departmentDropDownList outside the Employee module.
// If I could create a global variable deptID, that would be great!
getEmployeessResult.token=employeeService.getEmployeess(deptID);
}
I have attempted to use [Bindable] variables but without success.
I would appreciate your suggestions.
You can't really guarantee that the deptID will be set when creationComplete runs--it sounds like you're waiting for a server result--so this is probably not the best way to handle it.
One of the things you need to be careful of is directly referencing the full Module Class from the main Application, because the point of modules is that you should not compile in the module Class into the main Class (to reduce file size/load times).
So what you might want to do is create an Interface. This creates a "contract" between the main application and the Module without carrying all the implementation code with it. That might look something like this
public interface IEmployeeModule {
function set deptID(value:int):void;
}
Then, your Module might have code that's something like this:
protected var _deptID:int;
public function set deptID(value:int):void {
_deptID = value;
var token:AsyncToken=employeeService.getEmployeess(deptID);
token.deptID = value;//in case department id changes, you can determine if you still care
}
Note that, though global variables seem like a wondermous idea when your project is small, they are a very bad habit to get into. It can be almost impossible to repair a project that starts out with these and then grows to the point that no one can figure out exactly which of the hundreds or thousands of Classes that have access to a variable are changing it in the wrong way at the wrong time.
You ESPECIALLY don't want to use global variables with Modules, as they can cause really bad problems when the modules start fighting over the definition.
We solved this problem with the use of Cairngorm v2. Think of it as a message bus for ActionScript, one of several. In your departmentDropDownList_changeHandler method we would create a DeptChanged event with the ID as the payload, and send it on the bus to any and all subscribers to that message type. It worked pretty well for us, and made things more event driven, which in some circles is considered a good thing in itself.
#J_A_X I haven't had good luck with using Robotlegs out of the box with Modules. It seems that something goes wonky with the security contexts, even though it shouldn't. I had to use Joel Hooks' ModuleContext to make it work right, even though my needs were fairly basic.

Controlling dynamically created containers

Forgive me, I'm new to Flash Builder 4 and Actionscript 3 (actually, to programming as a whole beyond some very simplistic stuff). I have watched / read a bunch of tutorials, and started a project but now seem to have hit a wall. The answer is most likely simple, but seems to be alluding me.
How do I (or What approach should I take) to control visual elements, for instance, BorderContainer's, that I created dynamically?
As is, I have an Application containing a BorderContainer and a DataGrid. At runtime, 3 new BorderContainers (which are dragable, and resizeable) are created based on XML data that contains X & Y co-ordinates, and Height and Width values, and then added to the pre-existing BorderContainer. How would I go about getting the properties of these children BorderContainers to be displayed and remain up-to-date in the DataGrid (such as when they are moved/resized)?
My intentions in the future would be to have a custom component which displays a summary of these items in a separate area (think photoshop "layers" control, but much more simplistic), but wanted to get a better understanding of what's going on first.
Any input, documentation, examples, etc. is all appreciated. Again, I apologize for what may be an incredibly easy solution, or if any of my language is unclear, I'm new to this ^_^;
I would create an ArrayCollection of the BorderContainers with their various properties set (also make sure you call addElement on the parent BorderContainer). Make sure your ArrayCollection is declared as Bindable, then set it as the dataProvider for your DataGrid. Then specify the columns for your DataGrid based on whatever properties you want to display (height, width, etc). Now whenever the properties of the BorderContainers change, the DataGrid will automatically update.
Assuming a pure AS3 project, the best approach is to build a dictionary of your objects.
Let's also assume you've created identifiers for the components, or can easily create them at runtime.
var containers:Dictionary = new Dictionary();
private function _init():void
{
//some loop to create objects
containers[newObject.name] = newObject;
}
Later you can quickly access it by just grabbing the hashed index from the containers dictionary.
Now, assuming a Flex project, we have a few more approaches we can take:
DisplayObjectContainer implements getChildByName()
Group implements getElementAt, and numElements to iterate over, check names, and return value expected.
Personally, I still prefer the dictionary approach...
As for keeping things up to date, you can look into Binding (typically a Flex-only solution) or more appropriately investigate the events dispatched:
Event.RESIZE
Event.MOVE
etc.
In the handlers, just update your UI!
HTH, otherwise post more info and we'll see what we can figure out.

Adding and removing markers in openlayers on Drupal after page-load

I have to change the data set displayed on a map according to selections on the page and I would like to do this by creating several marker layers then switching between them based on user input.
For some reason I cannot add a layer after the map has been rendered on the page, seems like it shouldn't be that hard I think I may have the syntax wrong since the way Drupal sets up the map is different from straight forward openlayers.
Can I not get the map object like
var map = Drupal.settings.openlayers.maps["openlayers-map-auto-id-0"];
then add and remove marker layers from it? maybe there's another way of getting it?
Any help appreciated,
- Chris
The Drupal OpenLayers module only stores settings in Drupal.settings.openlayers.maps.
What you need is something like this:
var ol = $('#openlayers-map-auto-id-0').data('openlayers');
var max_extent = ol.openlayers.getMaxExtent(); // Or some other OpenLayers method...
...
The actual OpenLayers instance (as well as a copy of the map-specific settings) are stored with jQuery's .data() method. When you call $('#map-id').data('openlayers') you'll get back an object with map and openlayers members that correspond to the map settings and the actual OL object instance, respectively.
You might want to consider writing an OL behavior to handle your use case -- check out the default behaviors provided by the Drupal OpenLayers module to get a sense of how this works.

React to change on a static property

I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.

Resources