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.
Related
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.
I use Backbone.js. I have popup1, which creates popup2. popup2 is component-like and when it closes it triggers the event 'school_address:saved'. I need to send a request to the server on 'school_address:saved' event. I placed the handler which do so in the view of the popup1 (its instance still exists), but I'm not sure that this place is appropriate, because views are responsible for template UI logic, aren't they?.
What do you think is the best place for such code? And what would it be if I used Marionette.js?
We have been using BackboneJS for a few years now and have wondered about similar cases in the past...
Since BackboneJS (unlike other JS frameworks)does not enforce any common way of doing this, I would generally say this really depends on your implementation and application flow.
However, since these are popups/"application modals", I think it would help if you consider the following based on your current need:
If it is ONLY popup1 that can instantiate and display popup2, then have popup1 listenTo the events triggered by popup2.
If you foresee your application flow changes, and possibly having popup2 display elsewhere, or independently, I would suggest you listen to popup2 events from a view at a higher level (possibly one you have a route for). this way, both of your popups will be "independent", and you could recycle their code more easily and support greater flexibility as your application grows.
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. :)
I have an application built on Backbone. Since it is getting more complex I am evaluating a migration to Marionette but I am not sure on how to structure my views.
The existing application views are structured in this way:
BaseView = Backbone.View.extend({ ... })
The BaseView is the root of all views. It basically has a render function with basic stuff like: template rendering, page localization, active menu selection, etc
ListView = BaseView.extend({ ... })
Here the render method contains common code for all lists like loading and use of DataTables plugin, common events for edititem, additem, deleteitem, etc
FormView = BaseView.extend({ ... })
It manages generic forms using the Backbone.ModelBinder plugin and handles the form validation.
All my application views extend from one of the above to improve code reusability. For example I have an AccountFormView that extends from FormView where I have just the specific logic (a few lines of code) to handle account information. All the common logic is inherited from the parents views.
How can I obtain something similar using Marionette Views?
Thanks,
Fabrizio
Marionette's views are set up to handle the most common situations, and remove all the boilerplate code from solving these common problems:
View: a base view that can be used to build other views
ItemView: render a single model with a template
CollectionView: render every model in a collection, using the specified itemView
CompositeView: render a template as a wrapper around a collection view. supported nested / hierarchy structures
In your situation, it sounds like you'll be using a combination of these view types, depending on the specific scenario you're in. Instead of having a single view type to always extend from, though, you'll pick the one that makes the most sense for the current scenario and extend from that.
If you're looking for a way to add your own custom functionality to all views, that's also very easy - just add that feature tothe base Marionette.View or Backbone.View and it will be available to all Marionette views.
Be sure to check the documentation and code (it's split up in to many small files, so it's easy to read and understand) to see what methods Marionete provides for you, and what extension points it provides as well.
Hope that helps.
Should I use a router to manage changing of views? Currently, I'm using a 'parent' view to handle 'child' view management.
I have multiple 'child' views within a single parent. Child views/pages are changed by clicking links, which also modifies the URL. The changing of the displayed view is handled by the parent view -- NOT a router. The router in question has nothing of value coded, I created the 'dummy' router so I could use Backbone.history
I suppose you searching for some sort of validation that this is a proper way to construct your application. From the limited information you've given, it's hard to say, and even if you did provide some code, your question might not be definitively answerable, either.
So, let me just put it this way. I don't think there is any flaw in creating Backbone.js views that are composed of other views. I do think there's something wrong if you're putting everything related to view changing in the router--unless you're giving some sort of high level description there--like passing in views the parent view should show/attach events to/something else.
Another point. How are the child views related? Might some of them have a common (high-level) interface? Or does your parent view wrap disparate things, like a search form and some grids and labels? Or both? Is there some chance that this parent view might be useful on another page, or is it just going to manage this particular pages views, etc. All this matters.
I suppose there might even a reason to question as to whether or not your parent view should strictly extend from Backbone.View or should have some other sort of "base" class. But if it works and has some reasonable connection to being a "view" I wouldn't bother asking that question. As Jeff Attwood has said, "meta is murder". At some point these kinds of discussions become impractical, and when there are still important issues to be fixed or implemented, it's best to leave reasonable solutions be.
My thinking is if you are using the router to control and delegate things to the parent view, and let the parent view do it's things with it's child views, you probably fine.
You might want to consider passing in the child views through parent view's constructor in the router, though. It's often better, but not always better, to have a separate thing determine the composition of an object than to let the object create it's composition itself. But always go for transparency as well. Sometimes the freedom you get with dependency injection isn't worth the obtuseness of abstraction your code; especially if you're not quite sure what you're doing. Figure out what you're doing, then consider the abstractions you may not have thought of.
If you want to know if you are doing things consistent with the community's ways of doing certain things, you probably ought to go to the backbone.js Google discussion group and review or ask questions the questions there as well.