Previously, my Backbone router looked like this:
class App.Routers.ThingsRouter extends Backbone.Router
routes: '': 'index'
routes: 'previews/:id': 'show'
initialize: ->
@collection = new App.Collections.ThingsCollection
@collection.fetch
index: ->
view = new App.Views.ThingsIndex(collection: @collection)
$('#app-container').html(view.render().el)
show: (id) ->
@model = @collection.get(id)
view = new App.Views.ThingsShow(model: @model)
$('#app-container').html(view.render().el)
When navigating to http://localhost
, I would get the index
view rendered, and when clicking on individual elements, I would get the show
view rendered. However, if I went to http://localhost/things/1
directly (i.e. by typing in the URL), the show
view wouldn't be rendered. I realized that this was because the view was being rendered before @collection.fetch
was completing. I changed my router to the following:
class App.Routers.ThingsRouter extends Backbone.Router
routes: '': 'index'
routes: 'previews/:id': 'show'
initialize: ->
@collection = new App.Collections.ThingsCollection
index: ->
@collection.fetch success: =>
view = new App.Views.ThingsIndex(collection: @collection)
$('#app-container').html(view.render().el)
show: (id) ->
@collection.fetch success: =>
that.model = that.collection.get(id)
view = new App.Views.ThingsShow(model: @model)
$('#app-container').html(view.render().el)
Which works fine. However, there's obviously a little latency as the collection is re-fetched every time I switch routes. Is this good Backbone practice? Not sure if there's a better way of doing this.
This is a great use case for jQuery's Deferred() method.
Just create a Deferred object and attach it to the router. Then fetch the collection in the initialize method and call resolve()
on the Deferred object. Your index and show methods can subscribe to the done
callback and instantiate the view. This done callback won't be ran until the collection has been fetched. And if it has already been fetched, then it runs immediately.
class App.Routers.ThingsRouter extends Backbone.Router
routes: '': 'index'
routes: 'previews/:id': 'show'
initialize: ->
@collectionFetched = new $.Deferred
@collection = new App.Collections.ThingsCollection
@collection.fetch success: ->
@collectionFetched.resolve()
index: ->
that = this
@collectionFetched.done ->
view = new App.Views.ThingsIndex(collection: that.collection)
$('#app-container').html(view.render().el)
show: (id) ->
that = this
@collectionFetched.done ->
that.model = that.collection.get(id)
view = new App.Views.ThingsShow(model: that.model)
$('#app-container').html(view.render().el)