In many places in my app the following pattern happens:
I have tried both of the following implementation patterns:
Router handles fetching
View handles fetching
I dislike #1 since the router becomes a gigantic ball of Model/Collection fetching logic and seems to have too much responsibility. #2 seems like a better allocation of responsibilities (router just decides which view to show, view figures out what data it needs to fetch), but it does make the view rendering a little trickier since it's stateful now.
What does the StackOverflow community think? 1, 2, or something else?
This post is pretty old, but we were reviewing it earlier today, so in case anyone else comes across it:
To me, I really see 2 separate questions:
A bit of how we handle it mixed with some personal preferences:
I like Yuri's bullet points, with a couple caveats (indented bullets):
The main reason for the Renderer is to handle things related to that section, like cleaning up existing views to avoid ghost views, scrolling to the top on render (our MainContentRenderer does that), or showing a spinner in this case.
A psuedo-code-ish example of what that might look like, for:
Router:
routes: {
"profile": "showProfile"
},
showProfile: function() {
return new ProfileController().showProfile();
}
ProfileController:
showProfile: function() {
//simple case
var model = new Model();
var deferredView = model.fetch.then(function() {
return new View(model);
};
MainContentRenderer.renderDeferred(deferredView);
}
MainContentRenderer:
var currentView;
renderDeferred: function(deferredView) {
showSpinner();
deferredView.then(function(view) {
this.closeSpinner();
this.closeCurrentView();
this.render(view);
}
},
render: function(view) {
currentView = view;
$('#main-content').html(view.render().el);
}
closeCurrentView: function() {
if (currentView and currentView.close()) {
currentView.close();
}
}
Introducing a Controller also has the added benefit of testability. For example, we have complex rules for performing searches around URL management, picking between a results view and a new search view, and picking between a cached 'last' search result and executing a new search. We have Jasmine tests for the controllers to verify that all that flow logic is correct. It also provides an isolated place to manage these rules.
I tend to use the second option with three views, the container, a loading view, and a content view. That is, the container is instantiated by the router and during each render it looks at what it has on hand to display—sometimes provided by the router, sometimes by itself—and decides what view(s) to instantiate. A simplistic, contrived example:
ContainerView = Backbone.View.extend({
initialize: function (options) {
options.data.bind("reset", this.render, this);
},
render: function () {
var view;
// If the loading view is only shown once, e.g., splashing, then isReady()
// may be better here.
if (this.options.data.isLoading()) {
view = LoadingView;
} else {
view = DataView;
}
this.$("div.content").html(new view().render().el);
}
});
I like this approach because:
Clarification: The purpose of the view, in this case, is to understand how what is has to show should best be shown to the user. In this case, a bit of data still loading is best shown with a loading view, while ready data is best shown with a data view. Most real views are actually composing their display with many more views, e.g., depending on the user authorization different action containers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With