0

Bit of an odd one...

I have the collection:

class Store.Collections.Product extends Backbone.Collection

  url: '/api/products'

  model: Store.Models.Product  

With the view:

class Store.Views.Origin extends Backbone.View

  initialize: ->
    @collection = new Store.Collections.Product()
    @collection.fetch() 
    @model.bind('change:formatted', @render, this);
    @render()

  events:
    'change [name=origin]': 'setOrigin'

  el: =>
    @options.parent.$('.origin-input')[0]

  template: JST["backbone/templates/shapes/product"]

  render: ->
    $this = $(this.el)
    $this.html(@template(model: @model.toJSON(), errors: @model.errors))
    console.log(@collection)
    @collection.each(@appdenDropdown)
    @delegateEvents()
    this

  appdenDropdown: (product) ->
    console.log("append trigger")
    #view = new Store.Views.Products(model: product)
    #$('#history').append(view.render().el)

with the template:

  <div id="history"></div>

The collection works... the

console.log(@collection)

shows the data! however

@collection.each(@appdenDropdown)

Does not do anything, doesn't error, or through anything. It just doesn't do anything. I am trying to extract the data out of the collection! But it wont...

Chris Salzberg
  • 27,099
  • 4
  • 75
  • 82
Charlie Davies
  • 1,814
  • 4
  • 29
  • 50
  • Check http://stackoverflow.com/questions/8413500/backbone-js-populating-a-collection/8415515#8415515 or http://stackoverflow.com/questions/11459244/backbone-js-empty-array-attribute/11463190#11463190 – nikoshr Jul 19 '12 at 14:15
  • 2
    btw: why do `$this = ...` when YOU HAVE `this.$el`. I suggest reading through the backbone.js documentation. – jakee Jul 19 '12 at 14:35
  • and clear nikoshr's answer: collection.fetch is `asynchronous` and you render your view in the initialize method probably before the fetch returns, so the collection is empty. console.log is `wtfsynchronous`, so it regardless logs the collection as populated. What you have to do is to bind the render method to the collection's reset-event to make sure the view gets rendered also after the collection is populated – jakee Jul 19 '12 at 14:43
  • thanks, will take a look, the collection is not empty though, as it shows its data in the console.log which i was using just for testing – Charlie Davies Jul 19 '12 at 14:46
  • 1
    console.log showing a populated collection is explained in the second answer I linked. Summary : console.log plays tricks, clone the collection before logging and see what you get – nikoshr Jul 19 '12 at 14:47

2 Answers2

3

It's because there's nothing in the collection yet.

@collection.fetch() in the initializer is an asynchronous method. You have to wait until the fetch is complete before you iterate through the collection items.

The fetch() function takes an optional success callback that is fired when the fetch is complete.

So you can update your initializer code to wait until the collection is fetched before calling render. Here is the code.

initialize: ->
  @collection = new Store.Collections.Product()
  @collection.fetch
    success: =>
      @render()
Paul
  • 18,349
  • 7
  • 49
  • 56
  • can you stop everything until the collection is filled? – Charlie Davies Jul 19 '12 at 15:05
  • 1
    @CharlieDavies That kind of defeats the MVC to wait like that. Another way you could approach it is to have the view listen for the collection to reset to execute your render. this.collection.on('reset', this.render, this); So when you do your fetch (which triggers a 'reset' event), render will automatically be called. – Brendan Delumpa Jul 19 '12 at 19:46
0

The problem as others have mentioned is that fetch is asynchronous, but the solution is simpler: jQuery's deferred object:

initialize: ->
  @collection = new Store.Collections.Product()
  @collection.deferred = @collection.fetch()

  @model.bind('change:formatted', @render, this);
  @collection.deferred.done ->
    @render()

What's happening here is that when you call @collection.deferred.done, you're telling jQuery to wait until the collection is loaded before executing render on it, which is what you want. I think that should work.

A couple good references on deferred:

Chris Salzberg
  • 27,099
  • 4
  • 75
  • 82