I seem to often end up in a situation where I am rendering a view, but the Model on which that view depends is not yet loaded. Most often, I have just the model's ID taken from the URL, e.g. for a hypothetical market application, a user lands on the app with that URL:
http://example.org/#/products/product0
In my ProductView
, I create a ProductModel
and set its id, product0
and then I fetch()
. I render once with placeholders, and when the fetch completes, I re-render. But I'm hoping there's a better way.
Waiting for the model to load before rendering anything feels unresponsive. Re-rendering causes flickering, and adding "loading... please wait" or spinners everywhere makes the view templates very complicated (esp. if the model fetch fails because the model doesn't exist, or the user isn't authorized to view the page).
So, what is the proper way to render a view when you don't yet have the model?
Do I need to step away
from hashtag-views and use pushState
? Can the server give me a push? I'm all ears.
Loading from an already-loaded page:
I feel there's more you can do when there's already a page loaded as opposed to landing straight on the Product
page.
If the app renders a link to a Product page, say by rendering a ProductOrder
collection, is there something more that can be done?
<ul id="product-order-list">
<li>Ordered 5 days ago. Product 0 <a href="#/products/product0">(see details)</a></li>
<li>Ordered 1 month ago. Product 1 <a href="#/products/product1">(see details)</a></li>
</ul>
My natural way to handle this link-to-details-page pattern is to define a route which does something along these lines:
routes: {
'products/:productid': 'showProduct'
...
}
showProduct: function (productid) {
var model = new Product({_id: productid});
var view = new ProductView({model: model});
//just jam it in there -- for brevity
$("#main").html(view.render().el);
}
I tend to then call fetch()
inside the view's initialize
function, and call this.render()
from an this.listenTo('change', ...)
event listener. This leads to complicated render()
cases, and objects appearing and disappearing from view. For instance, my view template for a Product
might reserve some screen real-estate for user comments, but if and only if comments are present/enabled on the product -- and that is generally not known before the model is completely fetched from the server.
Now, where/when is it best to do the fetch?
If I load the model before the page transition, it leads to straightforward view code, but introduces delays perceptible to the user. The user would click on an item in the list, and would have to wait (without the page changing) for the model to be returned. Response times are important, and I haven't done a usability study on this, but I think users are used to see pages change immediately as soon as they click a link.
If I load the model inside the
ProductView
'sinitialize
, withthis.model.fetch()
and listen for model events, I am forced to render twice, -- once before with empty placeholders (because otherwise you have to stare at a white page), and once after. If an error occurs during loading, then I have to wipe the view (which appears flickery/glitchy) and show some error.
Is there another option I am not seeing, perhaps involving a transitional loading page that can be reused between views? Or is good practice to always make the first call to render()
display some spinners/loading indicators?
Edit: Loading via collection.fetch()
One may suggest that because the items are already part of the collection listed (the collection used to render the list of links), they could be fetched before the link is clicked, with collection.fetch()
. If the collection was indeed a collection of Product
, then it would be easy to render the product view.
The Collection
used to generate the list may not be a ProductCollection
however. It may be a ProductOrderCollection
or something else that simply has a reference to a product id (or some sufficient amount of product information to render a link to it).
Fetching all Product
via a collection.fetch()
may also be prohibitive if the Product
model is big, esp. in the off-chance that one of the product links gets clicked.
The chicken or the egg? The collection.fetch()
approach also doesn't really solve the problem for users that navigate directly to a product page... in this case we still need to render a ProductView
page that requires a Product
model to be fetched from just an id (or whatever's in the product page URL).