Passing data from main application view to some subviews - backbone.js

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.

Related

backbone js using multiple collection in view

I am quite new in backbone js . I was reading the documentation of backbone and I come up with this idea to use multiple collection in view.
If I have single view and I want to use more than one collection how can I achieve it?
how can the view understand multiple collection?
thanks.
Why do you ever need to use multiple collection inside a single view? Backbone's strength is it's modularity, which means you can develop the whole structure of your application by building it's components piece-by-piece. So in normal circumstances one view has one model or collection of models, but it's acceptable and it's often used the case when a collection has multiple views (for example a chat system).
To have multiple collections inside a single view is against backbone's modularity principle. Breaking up views to respond to only one model/collection turns out much more modular and reusable code.
So i suggest to break up your application into smaller pieces and operate on segment level, but if you really need to preserve the current structure you can do something like this:
var view = new MyView({
collection: {
users: new UsersCollection(),
organization: new OrganizationCollection()
}
});

backbone can a collection contain model objects of different classes?

I am just beginning to use backbone.js for a new crash project. My app has a dynamic (data-driven) user menu. Each menu option is a set of graphs/small tables, of mixed types. For example, a Sales Overview menu option can have a page with 2 pie chart objects, 2 line charts, a bar chart, and so on. I don't know up front what the menu options are going to be, nor what each menu option will entail.
I am considering defining a bunch of generic model "classes" by extending Backbone.Model - PieModel, BarModel, DispersionModel, etc. And corresponding View classes that can render an object of a type - PieView, LineView, and so on. Then I can assemble a page by putting these together as defined by the dynamic configuration. Each model instance's data url can be easily generated on the fly, via the dynamic configuration..
My first concern was if Backbone supports a Collection of mixed Model types. This is instigated by presence of a "model" property for a Collection - does it assume homogeneity? But it also says a collection can hold an ordered set of models.... model attribute can be polymorphic... a method to get "models" held in the collection. Should I be reading this as "model objects"?
A "page" to me really is a collection of such objects. I would like to create a Collection on the fly and populate it with instances of different model types. And then render this through a View. Or, create a View with an array of various model objects and render the View, bypassing the Collection all together.
I will appreciate your inputs on the design I have outlined, and good reference on backbone, and clarity on how to deploy a Collection in mixed model cases? Perhaps there is a different, smarter way to handle such scenarios...
Thanks.
Collections only really use their model attribute when passing plain objects into its adder functions (e.g. add, push). If you take a look at the source, each adder function passes the input through _prepareModel, which checks if the input is an instance of a Backbone.Model. If it's not, it tries to instantiate a new model using the collection's model, otherwise it just returns the input untouched.
So as long as you're always adding real Model objects to your collections you should be fine using different types.
However, if you're planning to use aggregate functions that act on model attributes (e.g. pluck) you may run into errors when the function tries to get at an attribute that doesn't exist in one type of model (though most of the time I think it would just silently fail, which might be what you want).
I am not sure if I have 100% properly understood your scenario, however, I am not convinced you are thinking about this in the right way...
In my opinion, your models should contain the data, and views should represent them. As such, in a sales context you might have a SalesData model which could be displayed in PieView, BarView or TableView. Try to completely separate display logic from data - the type of chart falls under display logic in my opinion.
With the above approach, each page would then contain a set of different views, which you could potentially contain in a master view if you felt the need. Each view would have its own model (or collection depending on how you structure the data), which you can then update/manipulate using the normal Backbone methods.
As far as I know it is not possible for a collection to have different types of models contained within it, but even if it was, I would probably not recommend it as it would complicate the code a lot.
In terms of learning resources, here are a couple:
Learn Backbone JS compeltely -- javascriptissexy.com - this one is very thorough but will take some time to get through.
Backbone patterns - much quicker to get you in the right frame of mind.

ExtJS4 - Store per panel instance?

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.

Backbone.js - View in Collection adding item to another (sibling) Collection

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.

Approach to hide/show Backbone model views

Help me to find best approach for managing Backbone views.
For example I have a collection of views MyCollectionView which consists of MyModelView - views of each model in the collection.
And what if I want to hide/show some models on the page?
Now I am following this way:
Use collection.each for every model
Inside of loop I call model function filter with some params
In this function I check model properties and call model.trigger 'hide' or model.trigger 'show'
And finally in the model view I use this.model.bind 'hide', this.hide, this where actually I use .hide() or .show()
This way seems me awful... Why do I need to do this long chain of functions and events. Does any simplest approach exist?
Thanks!
Your models shouldn't be telling views what they should do - they are supposed to represent data and shouldn't take part in controlling the application - so no wonders it feels wrong to you :)
The more elegant way would be to add a filter method to the MyCollectionView which would use underscore methods to filter the views you want to show/hide and do its job of well... doing that - picking which models should be shown. Then having hte array of matches just call a method for rendering the list and pass your array of models to it so it can render the views for matching models.
From my experience of creating such filters I can tell you that it might be much more efficient for longer lists to remove whole list do filtering on collection level and render again only the views which are matching the filter query. jQuery hide/show can be a bit taxing - though it's a concern you will have only with large amount of data/views.
Also! Take advantage of the underscore methods bound to the collection - you don't need to do collection.each(... you can just do
var matches = collection.filter(function(model) {
return /*matching condition*/;
});
(Also remember to use documentFragment when rendering lists and appending to the DOM pre-generated list of views rather then appending them one by one)

Resources