In my example I have a parent model (ModelParent) with two collections (A and B), which hold ModelA's and ModelB's respectively. There are associated views for each model (ViewParent, ViewA, and ViewB)
I would like a function in ViewA to add a new item to CollectionB.
What's the best way to do this? (couple of possibilities below):
Should ViewA be passed a reference to ViewParent, when it is created? How best to do this? (as far as I know there is now in build parent reference)
Should ViewParent be stored in the window scope, so that ViewA can refrence it like window.ViewA? (This seems wrong to me)
Another alternative than listening directly to events from your collection, is the use of an eventBus. Derick Bailey from Los techies wrote a nice article where he introduces the idea of communication between different components (in his case views) via an eventBus.
If you are firm with coffeescript - and possibly even if you're not - you should also check this nice extension from Adam Thurlow.
Side note: If your situation simply requires communication between the two elements an eventBus is most likely overkill, although the idea is imho ultra simple. Apart from that, I am of the believe, that a central component for communication is worth the effort as it simplifies messaging, supports decoupling and gives your architecture a reliable consistency.
I would consider thinking about it a different way. I believe it would be cleaner if you didn't have to pass references all around your views. Instead, use backbone's built in event model and keep the "add new item to CollectionB" logic inside ViewParent. When ViewParent instantiates ViewA, you could immediately bind to an event on it:
this.viewA = new ViewA({});
this.viewA.bind("some_event_that_requires_adding_to_collection", this.onViewAEvent);
Inside ViewA, whenever you want to add to CollectionB, just trigger the event:
this.trigger("some_event_that_requires_adding_to_collection", itemIWantToAdd);
Add additional arguments to the trigger call to pass them to any callback bound to the event.
Related
Writing my first Backbone app - came across a predicament wherein i am unable to choose which is the best way to move forward.
Scenario : User clicks an edit button , a new view is loaded . Approach is as below.
renderEditView: function(){
if(my.namespace.view){
my.namespace.view.render();
}else{
my.namespace.view= new editView({model:my.namespace.model});
}
}
Basically, i am assigning my view to a namespaced variable and resuing it as required. Didn't face any problems as such.
But some advocate recreating the View again using new editView({model:xxx}); whenever the edit button is clicked . i Would like to know which one is the better practice and why?
P.S: i am aware of the 'event ghosting' problem in BB apps, and the excellent solution provided by Derick Bailey .But still would love to know the pros and cons between the approaches.
This is indeed an opinion, because either way will work if you (as you mention) take care of cleaning up previous views if you decide to instantiate a new one every time you want to re-render. It's important to avoid duplicating lingering events from every instance that you want to replace by creating a new one.
Personally I have used both strategies and never had problems with them so far.
When re-using a view, I bind the view as a property to the controller object that renders the view, pretty much the same way you do it.
Theoretically, I don't see a reason to re-instantiate a view if it was already created before. It isn't that you really require a new instance, it's just that you want to re-render it.
Sidenote
For re-rendering views, Backbone Marionette offers regions, which are convenience objects that allow you to do things like:
var myView = new MyView();
var region = new Marionette.Region({el: "#container"});
region.show(myView);
In case you would decide to instantiate a new view every time, these regions take care that previously rendered views are properly cleaned up.
Please consider following Backbone application structure:
AppView
Subview
FirstSubviewInQuestion
Subview
Subview
SecondSubviewInQuestion
Application view creates and stores a collection of special items. At a certain time the first and the second subview needs to get access to that collection. Now what is the best way to pass the collection it to them?
I myself see some ways, yet each has a downside:
1) Create instance of that collection not inside the App View but even more globally and then pass it around as dependency (project uses RequireJS; so it would be passed to AppView and both Subviews).
Downsides: app view is supposed to be the top most element in application. Yet suddenly we have some instances of collections floating above.
2) Or I can do following:
// some subview
var collection = {};
Backbone.trigger( "item:getSpecialItems", collection);
// now the collection has data
// app view
this.listenTo( "item:getSpecialItems", function(obj) {
// copy collection to passed object
_.extend(obj, this.specialCollection);
});
Downsides: we are triggering a global event. We know that only the app view would respond, but it feels like a bad design. Plus, this way to pass the collection seems like a hack.
Maybe there are some other clever ways to do it?
I would say #1.
app view is supposed to be the top most element in application
Right, but you're talking about (I think) a Collection, not a View; the two are totally separate parts of your application (in MVC the first is the "M" and the second is the "V"). By definition, if your views are driven by your data, the data must be "higher" (in the dependency tree) than any view, so I don't see anything wrong with that approach.
If your goal is to get a shared instance of the collection, my approach would be to pass that down from the parent to "subview" and from subview into its children. However, I wouldn't necessarily use require to pass around a singleton of this collection.
You could also pull out all of the logic from the view regarding the special methods for "creation and storage of special objects" into a helper class whose sole domain is just this. That helper then becomes a utility available from outside the view hierarchy, perhaps even globally or via require.
I am currently using backbone to implement my app. As part of memory management, I will trigger a teardown of all the views when I am switching views
teardown: ->
for viewName, view of #subViews
view.teardown()
for object, events of #objectEvents
#_handleObjectEvents("off", object, events)
#off()
#remove()
#undelegateEvents()
#
Is this approach sufficient to ensure that most of the memory issues are resolved? The challenge I see here is that I need to track all the subviews of each view and call teardown for all main views and subviews as part of the cleanup.
I did some searching and found that backbone also has these two events: 'listenTo' and 'stopListening' where we control the binding of events to models at the view level.
view.listenTo(model, 'change', view.render);
view.stopListening(model);
My questions is, is there an overlap between my teardown implementation and using 'stopListening'? Can I just solely use 'stopListening' for memory management?
The short answer is yes, there is an overlap.
The more complicated answer is listenTo/stopListening methods introduced in Backbone 0.9.9 already use on/off methods but with some useful addition – they store current event listeners in internal object called _listeners.
The benefit of using this object is that you always know full list of all your listeners – you can iterate over it and remove specific elements from it (remember that a listener is just a function and a function is just an object).
So, you can call it this way:
this.stopListening(emitting_object, ["reset", "add"]) // Removes listeners for "reset" and "add" on emitting_object
this.stopListening(emitting_object) // Removes all listeners on emitting_object
this.stopListening() // Iterates over _listeners object and removes all listeners (probably the most usable case)
So, using this method, you can convert your teardown method to something like this:
this.teardown = function(){
this.stopListening();
...
}
I'd recommend using listenTo method. The niceness of it is that when you use the remove method on your view, it will automatically unbind (call stopListening) on what it's listening to. According to Derrick Bailey, it also unbinds the events under the events property.
What I will do, since I am in the process of upgrading my app to 0.9.9 from 0.9.2 (which actually still works so far), is just switch around all of my ons/offs to listenTo and stopListening. I also, mostly, have close methods on there. I will, however, still call undelegateEvents, just in case. Doesn't hurt to know that you're still getting rid of the event listening.
I know this may sound dumb to most of you, but i'm in the beginning of understanding backbone.js. All i mean to say is that can we use more than 1 el/element ( whether it's div,ul or any element) in a view?
More specifically saying when in a view/model we define el like this:
el: '#some_id'
can i have more el to work with in a model/view and how can we access each of them?
right now with only 1 el, we use this to point to that specific el, but how to do if we can work with multiple el?
hope you guyz get what i'm trying to say.
The easiest way might be to use a single container element; you can access the child elements using this.$("childelement").
You could also add more elements to your view class, or create composite views (Marionette has some examples of composite patterns). One of the nice things about Backbone is that it's very minimalist, in that it forces very few constraints on how you structure the site. You can really extend/modify it any way you like.
The main uses of View.el are:
Wiring up events. The view's events object applies listeners against the el container.
View.remove removes el from the DOM.
Basic helper methods this.$el and this.$("selector") are basic helper methods for accessing the DOM
These are all methods that you can easily override, or create your own implementations of.
First, models don't know anything about el's.
Second, a view instance can only be attached to one el. It can manipulate elements inside that one (this.$("#child")). It can manipulate elements outside itself, although that is considered bad style. Also, you can have multiple instances of that view, each connected to it's own el.
I hope that helps.
I'm really new to the MVC pattern in Ext.
I have a tabpanel with multiple instances of the same component (let's call it product), each should call the server when it's opened, with an id parameter.
Right now, in order to create these tabs - I use this in the Product controller
Which creates a new instance of a view, but I feel like it's really incorrect.
createMainView: function (opts) {
return Ext.widget("productDisplay", opts);
}
I call it from my "main" controller, like this:
var tab = this.application.getController("Products")
.createMainView({ productId : id, closable: true })
tabs.add(tab);
tabs.setActiveTab(tab);
What's the correct way to properly use multiple instances of a view, each having an instance of one store and behavior (via the controller).
Can I use one named store for them (with a js file under app/store/product.js)?
Should I manually call load on the store from the controller (to pass the productId), or is there a nicer way?
It's kind of very broad and interesting question which require big and thorough explanation (which you can find btw on the Sencha.com in their guides and manuals). I would like highlight couple points so you have something to start with:
Stores are usually global objects. You don't keep two instances of one store in general. You can use filtering (local or remote) if you need to present information from the that store in several different views. The only time you would need to clone store is if you want to present different information from that store in 2+ different views at the same time.
Controllers are usually spawned by main application object. You don't have to do anything special - just list them in the controllers: [] property. And then spawed when application is launched (before their views are created and rendered). Keep that in mind.
If you have modal view - it's ok to create it manually and either re-use it or destroy and re-create later. You can add filtering and loading to controller which creates these views. And you can re-use same view/controller objects for different tabs if you want.
If your views are presenting one instance of an object (like one product is displayed on each tab) - don't attach stores to those views. Just pass them individual model (record).
I would recommend creating the stores that relate only to that view instance inside of the view's initComponent method.
Your controller's control handlers should be coded in a way that they can differentiate which view dispatched the event. This should not be too difficult because almost all view events contain a reference to the component which fired the event. You could then use the relative query selectors, e.g.: myEventFiringComponent.up('anotherComponent') or myEventFiringComponent.down('anotherComponent') to get a handle on a different component in the same view if you need to.
Please see this post for a full explanation.