12
votes

I have an application that does CRUD for a Collection of Models. There is a DisplayView for each model that is always visible. There is also an EditView that is visible only when the associated DisplayView is clicked on.

The DisplayView and EditView appear inside of different parent views. Right now I am using the "event aggregator" pattern to tell my application to render the EditView when a DisplayView is clicked. Pattern described here: http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

When one of my DisplayView is clicked it fires an event that the parent of the EditViews listens to. When it receives this event it renders the appropriate EditView, based on the model for which the event was fired.

This works well for most of my application, but is particularly cumbersome when I want to have the EditView change position based on the absolute position of the related DisplayView in my application. Rather than have the DisplayView directly control the position of the EditView, it triggers a "please reposition yourself to these coordinates" event. Such direct communication doesn't feel like something that should be broadcast to the entire application. I'm starting to wonder if for my case I should just have a reference to the appropriate EditView as a property of each DisplayView rather than decoupling them.

The problem, as I said, is that they are rendered inside of different parent views. The DisplayViews get rendered in the HeaderView while the the EditViews get rendered in the ContentView.

How do others handle situations like this? The EditView in some ways belongs to the DisplayView, but that does not match the way my application's DOM is structured. Assuming I do create a direct link between each EditView and DisplayView, how would I handle show/hide of the EditView? Would the DisplayView also need a reference to the ContentView container, which it would render explicitly with the appropriate EditView as a parameter?

3

3 Answers

15
votes

As much as you can, definitely avoid views holding references to parallel views (as oppose to parent / child views) and modifying each other, this can quickly turn into spaghetti and make your code much more fragile. Instead, the following patterns allow your different views to stay decoupled while still get the job done.

Global Notifications / events

This is the one you mentioned. It works but like you mentioned is less than elegant because it broadcast itself in the global scope unnecessarily

Binding / Observer Pattern

make an object named editViewPosition in your controller and expose methods to let the display view change the values of the editViewPosition. The EditView could then listen for and observe changes in the editViewPosition and update itself accordingly. The strength of this approach is that later you can have 5 different EditViews all observing the same property editViewPosition on your controller and update themselves accordingly, and there would be nothing you need to change in your DisplayView for that to happen.

Delegate pattern

Instead of directly hooking up the views and calling methods on each other, you can allow the display view to have a delegate property, which can be set by the controller to be the edit view. When the DisplayView wants its edit view updated, it will check if its delegate exists and implements a predefined function, if so, it will call the function. This approach is more coupled than the observer pattern but still allows a high degree of decoupling (and later for instance you can swap an entirely different view in there instead of your edit view and the program should still work.) Weakness of this approach is that you typically only have a single delegate, but it is more direct than any of the other patterns mentioned.

Manually maintaining a list of objects to notify

This is almost an extension to the delegate pattern. Basically you have an array of EditView like objects in your display view, and whenever needed, your display view will go through the array and call method on each of those objects. This array would again be populated by your controller.

Conclusion

Personally, I'd most likely use the binding and observer pattern for your use case. In general, views that are parallel (no direct parent / child relationship) should not hold references to each other and should only communicate via a common controller / event / notification / other super-structure that they share.

1
votes

There is another option, in addition to those enumerated by Tony, which is "bubbling up" an event that is triggered when the DisplayView is clicked to the closest common parent view of the DisplayView and EditView. With the event you pass the coordinates of the display view and the model it represents. Then the common parent directly calls a method on the EditView to reposition it and render the proper model. Thus no explicit references between parallel views, and no global variables.

Bubbling events up multiple view layers gets messy using backbone's built in trigger and listenTo methods, but the Backbone.Courier plugin makes it very straight forward:

https://github.com/rotundasoftware/backbone.courier

0
votes

If you already have your application decoupled keep it that way. Introducing direct dependencies between views will make your application less maintainable and overall more frustrating to work with.

Events are the way to go here.