Backbone + Rails collection fetching
backbone.js, ruby-on-rails
Solution
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)
Problem
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.