When to use a Backbone.View vs. Backbone.Marionette.ItemView? - backbone.js

I'm working my way through my first attempt at using Backbone.Marionette and wondered if there is any reason to use a Backbone.Marionette.ItemView when a simple Backbone.View will suffice?
Thanks!

Marionette's ItemView provides a default render and close method which do a few things for you.
render does the following (amongst other things):
1) Marks view as !this.isClosed (related to showing views in Regions, another Marionette concept)
2) Triggers before and after render events
3) Calls a serializeData method to get data (defaults to data from either this.model or this.collection.
4) Gets the template (possibly from TemplateCache) and renders via Marionetter.Renderer
5) Binds UI elements
In addition to this "free" render method, you also get close functionality.
Of course, if you are using a CollectionView/ItemView combination (which is very powerful) or a CompositeView/ItemView, you HAVE to use an ItemView.
Outside of those cases, one big benefit is that you KNOW your view is going to be compatible with Marionette Regions. Ideally, in a Marionette app, you use separate regions to show your views, like so:
var myView = new MyView();
// render and display the view
MyApp.mainRegion.show(myView);
// closes the current view
MyApp.mainRegion.close();
If MyView is a Marionette ItemView, this will work like a charm. If it is just a raw Backbone View, you will probably need to do some work to make sure that it works as planned.
In my apps, I typically just opt for using ItemView as the basis for all of my non-Collection and non-Composite Views.

Related

When should I not use a self-rendering view?

I've been studying this Backbone tutorial and came across this bit of code:
var LibraryView = Backbone.View.extend({
el:$("#books"),
initialize:function(){
this.collection = new Library(books);
this.render();
},
...
});
The author explains that this.render() makes the view render when its constructor is called.
When would you not want the view to self-render?
The render method does nothing more than adding HTML to the DOM by using jquery's html or append methods. Where you choose to call the render method on the view is an architectural choice.
Backbone is said to apply the MV* pattern where models are bound directly to the view.
As such, the view would render itself upon instantiation, and have the ability to render itself when the model changes.
However, Backbone leave a lot of decisions to the developer and is a flexible library. There is nothing that technically prevents you to use control objects that manage the flow of the views; as such, another object can instantiate and re-render the view as well.
var view = new Bb_View();
view.render();
In the end, render is just a method on the view, and you can define your own methods as well. An example reason why I would call custom methods on the view from outside the view, is when I keep reference to an array of views.
As an event occurs, I loop through the views, I might add some conditions, and then call the custom method on particular views based on the condition.

How to update the view without re-rendering in marionette.js

How to update the view with the model.fetch(), i.e when i fetch the model, the view shouldn't be destroyed or re-rendered. It should just update the new model into view with reseting the previous model.
this.model.fetch({success: this.render.bind(this)});
This code is re-rendering my view..How can i just update the view with new model
Thanks.
There are multiple ways of updating the view based on your need.
If the view is fairly simple, and all that is needed is to automatically update the view on model's fetch, rendering the view again is the simplest(Backbone's sync event on the model can be used, and model events can be handled declaratively using Marionette View's modelEvents hash -http://marionettejs.com/docs/marionette.view.html#viewmodelevents-and-viewcollectionevents)
modelEvents: {'sync': 'render'}
If you have few model attributes that change in a complex view, you could directly update the dom elements via jquery by listening to change events on the model attributes:
modelEvents: {'change:name':'nameChanged'},
nameChanged: function(model, value){ this.$('#name').val(value);}
If Two way data binding between view and model is needed, frameworks like Backbone.stickit can be used to this purpose - https://github.com/NYTimes/backbone.stickit#usage
Whenever you establish a double binding with your model attributes to the templates, your views need to be rendered again to show the changes. So, you can't avoid rendering to show the updated status of your model.
But what I would suggest you to do is to divide your view into subviews. Since, you are using marionette, you can create a main layout which contains all the regions and for each small region, you can define a view .
For example , suppose I have a basic form with my name, current time and age . All of these variables are stored in a model . So, you have a scenario where your name and age hardly changes but the current time is changing every millisecond causing your whole view to re-render which is bad in terms of performance as well as to the eyes.
So, in order to solve the above scenario, if you could create a separate view for the current-time element, you can render is separately and other elements don't need to be rendered again and again. You can always define a separate view for a single button element if you think that its functionality can be abstracted.

Handle Views in routing backbone js

What is the best way to switch to a different view when user navigates to a different url. In angular there is ng-view that takes care of this and inserts corresponding templates and in ember its all route based.
Is it better to just hide other views elements on routing using css or destroying other views and inserting current view?
EDIT
It would be great if someone could give an example how to re-render the view on navigating back to it again and restoring its previous state.
Eg.
if you have a check-box in a view that user can select to add some item to the cart , but in the middle he/she moves to some other url and then comes back, that check-box should be checked.
I would have a main content view with subviews and call remove on it, which is responsible for cleaning up any subviews too (calling remove on them first and going up the hierarchy tree). The concept of subviews doesn't come for free with backbone but isn't hard to implement. And finally attach a new content view.
This ensures you can cleanup and the browser is using a consistent amount of resources.
I would abstract this into some kind of layout view which has a content subview and a function like setContent(view) which handles the remove of any existing content view and the attach of the new one.
Personally I would have a router with sub routers in modules, e.g. a main router which finds a route starting with "checkout" and passes it over to a sub router in the checkout module which is responsible for attaching a new content view.
In Backbone the implementation is up to you which is both good and bad, depending on how nice you do it ;)
Always remove the view as opposed to just hiding it. If you don't remove (and unbind) your views properly, all bindings, handlers and references to models/DOM elements will linger around.
Depending on the size of your app, you can have a module that handles layouts (as suggested by dominic-tobias), or have a method on the router that takes care of this for you. At its most basic, this method (let's call it _switchView) takes a view and holds onto an instance of the currentView. Upon view change, it removes the current view, sets the new view to the current view and then renders it to the DOM.
Something like this:
_switchView(view) {
this.currentView && this.currentView.remove();
this.currentView = view;
this.$rootEl.html(view.render().$el);
}

Why should you put custom event listeners under intitalize in backbone?

I'm wondering why you need to put a object.on(event, callback, [context]) specifically within an initialize method in backbone, and not somewhere else?
Is it because the initialize method runs automatically -> and the listener starts to listen automatically for that reason?
A better way to design a backbone application is to separate data loading code and views construction.
This can be done by using a separate controller(A custom javascript object) that does the data load part i.e, initializing your models or collections. The controller loads the data (models/collections) necessary for a particular view.
The view instance is then created. Data required for the view can be passed as parameters to the view. One of the better ways is to pass it to use the initialize method which gets invoked automatically during the instantiation of the view.
Also we will expect the view to reflect the changes that model/collection undergoes. Its good to define what events of the model/collection that must be listened to in the intialize method since:
You know that it will be called for sure.
You know for sure that the event binding will be set, since you can reach a view from more than one way like thru a route or a click event on tabs for instance (though better designing is that you have a center piece of code which handles all the ways you reach a view).
Also set the rules for event bindings during the initialization itself.
You can also refer to the below link that talks about decoupling view from data loading.
Should the backbone router or view handle fetching data and displaying loading status?

Backbone: what's the point of a View that's never rendered?

I'm trying to learn Backbone from someone else's Backbone app in combination with the documentation. There are two things I don't understand about it that I can't figure out from the documentation. Hope someone might be able to explain...
Gist of the app
It's an app where you can drag and drop company names into boxes.
The app has a Company View, a Company Model and a Companies Collection.
It also has a Bucket View, a Bucket Model, and Buckets Collection
It also has a general App View that's not associated with any model.
Problems...
First- Is it weird that a view would never be rendered?
The App View is instantiated like this
window.App = new AppView();
and it gets different things going in the app (populating the buckets with company names etc and setting up some events). However, the App View is never rendered. The Bucket View and the Company View, on the other hand, are both rendered at different points in the code. For example,
var view = new BucketView({model: bucket});
this.$("#bucket-list").append(view.render().el);
But never that App View. This App View also has no render function. So is it weird that it has a View that's never rendered? Should its code be somewhere else?
Second Considering that App view is never rendered, is there any reason why they would have to change the default setting of el to the name of the main container div in the layout? For example, in the App view, they do this...
el: $("#mainapp"),
However, if this view's never rendered, is there any reason to change the default setting of el?
When you create a Backbone.View instance normally, it will immediately generat an el on the view for you. This is used as the placeholder for all of the HTML that the view will manage, and you generally populate the view's el with your HTML in the render method, as you've already noted.
In some cases, though, you don't want to render a completely new set of HTML elements. Instead, you'll want to manage some existing HTML that's already part of the DOM. In this case, you can easily attach a backbone view to the existing DOM element by specifying the el in the constructor, as you've shown with the el: $("#mainapp") line.
When a Backbone view sees that it already has an el, it does not generate it's own. Instead, it uses what it was handed.
So, to directly answer your two questions:
1) First- Is it weird that a view would never be rendered?
No, because ...
2) if this view's never rendered, is there any reason to change the default setting of el?
The question is slightly off in a manner that that can't be directly answered. In reality, the view is never rendered because they are setting the el in the call to the constructor.

Resources