I have the following router
var watchlistEditView = new WatchlistEditView({ });
router.on('route:editWatchlist', function(id) {
console.log("routing to editWachlist");
console.log(id);
var watchlistEditView = new WatchlistEditView({ });
watchlistEditView.render({id: id});
});
I do notice that I have multiple instances of the view so when I perform an operation, it does trigger on every instances, which is not what I want.
Is it ok to create the new view each time the router get called?
How could I prevent the view to get instanciated multiple times?
Thanks
This is something to make a good research as it involves many things. Memory management, listener bindings and things that actually backbone is handling every moment you create and destroy a view.
I recommend reading this article.
https://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
Actually I create and destroy what I need using backbone marionette framework and behind the scenes when working on layouts and backbone views it makes several checks as if the view is rendered if I am adding a new view to a region with a view already, and other things.
I recommend keep on creating them, and looking when to destroy them when it is necessary.
Related
I use backbone in most of my projects, and I think I know exactly what the M V C means: M for abstract of the data, V for representation and C for handlers.
However during my current project I found that there are a lot of views interactive with each other, while there are little modes(data with the server).
For example, I have there complex view named V1 V2 V3, when user do something in the V1, V2 should respond accordingly, so does the V3 and etc, and at the last step, a requst may be made to request data from the server. And most of the request are used to fetch data rather than modify data.
It does not like the common style: one(or more) view for one model such as the CRUD operation.
Now I have two ideas:
1 Virtual model
Create a backbone model to represent the state of the whole application, bind this model to all the views. Sounds like make the application as a state machine.
While it is not easy to describe the application with different states.
2 Use the event mediator
Use the event mediator to register/un-register different events, then the views can trigger or respond by different events.
While how to define the events to avoid insufficien or excessive, in a word to make the events orthogonal is not easy. Or I have not found any instructions yet.
Is there any other alternative solutions?
I think that it is a quite relevant question actually.
Create a backbone model to represent the state of the whole
application, bind this model to all the views. Sounds like make the
application as a state machine.
This doesn't seem like a very good idea, if the model isn't a consistent representation that corresponds to a specific backend resource.
Ideally, a view is a representation of a single model or collection. When a view would be bound to a model with unrelated properties, this doesn't seem too practical to manage in all circumstances, also due to an unforseeable future.
Use the event mediator to register/un-register different events, then
the views can trigger or respond by different events.
I don't think that it is overall a good idea to make views respond to custom events but this is personal for me. When apps become larger, assigning too much responsibility to complex views can become a mess; therefore I take it as a general rule to limit the task of the view to:
Rendering templates;
Activating plugins (if they're specific to the view);
DOM event binding;
(Model binding);
Methods internal to the view (related to the DOM);
Triggering custom events to notify other listeners when further action is required after interaction with the view;
In any case, in my experience I've found that it is practical to use a custom presenter/controller to instantiate / update views on custom events, and not let the view itself worry about these things at all. It keeps them clean and you always know what you will find there.
The views 1, 2 and 3 as you mention can be re-rendered from the presenters.
The presenter does something like:
"I get some data from a service and give it to some of my views that require
them. If something changes, I'll let them know"
I usually have 1 presenter per related set of views.
I like this approach because:
It keeps logic for related views centralised;
When an event is triggered, the presenter needs to listen only once for all the views that it controls;
Presenters are given the authority to talk to each other, which is to my feeling easier to control than when all views would start talking to each other through each other;
In simple cases, all of this doesn't matter too much probably. But when building a larger application, I found that it can become a mess.
My two cents
I have there complex view named V1 V2 V3, when user do something in the V1, V2 should respond accordingly, so does the V3 and etc
It doesn't seem like you have 3 views, but in fact 1 view with 3 interrelated sections. I would use one super view that renders 3 child views, and listen to view events. For example:
Backbone.View.extend({
initialize: function () {
this.v1 = ...;
this.v2 = ...;
this.v3 = ...;
this.v1.on('user do something', this.v2.respondAccordingly);
this.v1.on('user do something', this.v3.soDoesEtc);
}
})
And in view 1:
$('button').on('click', function () {
self.trigger('user do something');
})
This is a problem that many Backbone developers face.
What I've done in the past is to have a baseModel/baseCollection, and treat them as an abstract class/interface from which other models/collections extend, respectfully. These base objects would contain a listener/trigger methods which I could then use across my application to have changes in one model/collection be able to initiate updates on collections/models (thus triggering view changes) respectively as I chose. Using this method allowed me to compose my application by having the appropriate objects listen/broadcast events appropriately as I wished.
One of my friends created a server-side JS state machine which would initiate super-models (app-level model which in turn could trigger sub-view model/collection updates).
Of course, Marionette provides a framework that makes this a little less manual and allows you to get back to writing app-code.
One of the joys and burdens of Backbone.js is that you have all the flexibility you want. :)
In an MVC controller is what holds the business logic. In backbone the
controllers have been renamed to route. Now there are couple of things
which bring in confusion.
Model should have business logic.
Collection is the collection of models.
Views are where the templates are rendered, and most of the DOM
event handling is done.
Apart from routing, what do the routers do? And where should more
of business logic go to the routers or to the models?
Do the views perform anything extra other than rendering DOM
variables?
Yes they should, also should be the ones with a "link" to the Backend, to do the CRUD, but can also validate object state, ensure backend and frontend model sync and other things.
Correct again, but they also have a very nice use that is to fetch lists of models from the server very easily.
They should also attach event handlers to HTML elements and models and react to those events accordingly.
4.They handle all URL changing events and direct them to display the proper views for that URL, routers give you the opportunity to change your page completely and keep track of the URL changes by using Backbone.history, so Back and Forward Browser buttons will keep working.
They do the URL mapping.
It's an awesome framework, I can't live without it anymore.
IMHO:
You don't need to use routers et all, if you use them the should just route.
Views should contain all the DOM/Model-Event listeners.
Some Ideas on structuring: http://ricostacruz.com/backbone-patterns/
I believe that in the data-driven programming paradigm, the router and the view should be isolated from each other, and they communicate to each other only through model changes, which they both subscribe to.
However, the various tutorials online all do this differently. I have seen code that instantiates a view inside the router's initialize method, and hence giving the router a way to access the view. I have also seen code that passes the router to the view, so that the view can listen on the changes on the router's route events.
I don't believe either approach is right, as it breaks the separation of concern. As I am new to Backbone, can someone more experienced and knowledgable confirm?
I don't see any problem using the Router to instantiate Views, actually is the way I use to work. The Router detects a new URL and detach any not used View and instantiate the requested ones.
Passing the Router reference to the View can be uglier, not to say too much effort to keep the reference traveling between Views and SubViews.
But in the other hand Views have to be able to send signals to the Router to change the navigation so I also go for the easy way and make the Router globally visible for all elements in my App and I can call App.router.navigate() from any View without having to remember to pass the reference explicitly. This decision can be defended because only one Router instance is allowed in any Backbone application.
In some projects I have used a global event dispatcher, which passes the routing events to the views.
window.Dispatcher = _.extend({}, Backbone.Events);
In the router you can bind to 'all' event which captures route:* events:
initialize: function() {
this.bind('all', this.onRoute);
},
onRoute: function(route) {
Dispatcher.trigger(route);
}
In your views you can bind to Dispatcher events and react to route changes:
initialize: function() {
Dispatcher.on('route:index', this.onIndex, this)
}
This is similar to having a model that changes when the route changes, but I feel this is easier to manage, and also doesn't require passing the router to views. Not sure if this is the right way to do it, but then again Backbone enables you to do things in many different ways.
I got several different views which are mostly built up like (layout-component, layout, main view).
When I now switch between different views the whole page has to get rerendered.
Wouldn't it be better to cache at least the layout view in "window" and reload them?
Something like a singleton pattern for backbone views?
How do I do this?
Is a simple:
window.MainLayoutView || window.MainLayoutView = new MainLayoutView({ el: 'div.main' });
enough?
Is there anything else I have to think about?
Yes, there is more to worry about. If you do something like this:
window.MainLayoutView.render();
$(x).html(window.MainLayout.el);
// And later...
$(x).html(someOtherView.el);
// And later still...
$(x).html(window.MainLayout.el);
you'll find that all your events inside window.MainLayout are gone. If you want to cache the instantiated view and swap it in and out, you're going to have to arrange delegateEvents calls to rebind all the events all the way down the view hierarchy.
Compare the behavior of these two examples and you'll see the problem:
http://jsfiddle.net/ambiguous/Mrt3C/ (no delegateEvents calls).
http://jsfiddle.net/ambiguous/Mrt3C/1/ (with delegateEvents calls).
Generally you don't bother trying to cache views, just remove them and recreate them as needed.
I am writing a module based javascript website, backed by a Mysql DB.
Each module talks with the DB through PHP.
On the UI, for simplicity, the module on the left will display all relevant rows, with edit(write) functionality. The module on the right display the data from the same DB, with write access too.
So each would affect the other in each update.
I'm told backbone would be a good framework. Then, I have read the todos example, and understand how there are item->itemList, view->viewList, etc...
But now, the question is, how do I apply it to this situation?
Which is an item, a view in this situation?
The module on the left and the module on the right are Views, each of which can be made up of other views.
Within the model don't store a reference to the view (as I've seen done in some of the examples):
this.view = view; //view being passed in as an arg
The reverse (a view storing a reference to a model) is okay. Your views should be doing most of the work, listening to and responding to model events. Thus, in the view initialize method you might:
model.bind("interesting-event", function(){
//the view updates/reacts to the model.
});
Also, never add a model to two collections (just one ever). When a model is assigned to a collection, Backbone sets a reference on the model that points to the owning collection.
Incidentally, the a-model-cannot-belong-to-two-collections issue is the reason why you don't want a model referencing its view. A model can have many views on one screen.
Backbone is perfect for your needs. Start with a very basic version of your app and keep fleshing it out. All the while keep reading about Backbone online. I read everything I could find (there's not a whole lot, not really). The key concept is simply event based programming (much like you'd use in VB or lots of other platforms). It's a lot of trial and error, but you'll make sense of it with practice.