Search code examples
ruby-on-railsbackbone.jscoffeescript

rails backbone coffeescript ListenerId defining


On my index_view i want to dynamically add item when it is created( function for creating is in index_view - create_item() ). Something im doing wrong. In my index_view i have collection view and it has no ListenedId, which i need to listen (i think) and update my view when new item is created. On index_view initialize i have not-working function, how to make it work? @listenTo is not working, it has no ListenerId, or where i could define it?

thi is my index view:

//app/assets/javascripts/backbone/views/inventories/index_view.js.coffee
Sprint.Views.Inventories ||= {}

class Sprint.Views.Inventories.IndexView extends Backbone.View
  template: JST["backbone/templates/inventories/index"]

  initialize: () ->
    @options.inventories.bind('reset', @addAll)
    @options.inventories.on('add', @addOne) //not working

  create_item: (attributes) ->  //this is called from my other models view
    this.collection = new Sprint.Collections.InventoriesCollection 
    this.model = new this.collection.model(item: attributes.name, user_id: router.stats.models[0].attributes.user_id)
    this.collection.create(this.model.toJSON())


  addAll: () =>
    @options.inventories.each(@addOne)

  addOne: (inventory) =>
    view = new Sprint.Views.Inventories.InventoryView({model : inventory})
    @$("tbody").append(view.render().el)

  render: =>
    @$el.html(@template(inventories: @options.inventories.toJSON() ))
    @addAll()
    return this

this is my model:

   //app/assets/javascripts/backbone/models/inventory.js.coffee
class Sprint.Models.Inventory extends Backbone.Model
  paramRoot: 'inventory'

  defaults:
    item: null
    user_id: ""

class Sprint.Collections.InventoriesCollection extends Backbone.Collection
  model: Sprint.Models.Inventory
  url: '/inventories'

this is my router:

   //app/assets/javascripts/backbone/views/inventories/inventory_view.js.coffee

class Sprint.Routers.DropsRouter extends Backbone.Router
  initialize: (options) ->
    @inventories = new Sprint.Collections.InventoriesCollection()
    @inventories.reset options.inventories

  routes:
    "index"                : "index"
    ".*"                   : "index"

  send_item_to_inventory: (attributes) ->
    @view = new Sprint.Views.Inventories.IndexView(inventories: @inventories)
    @view.create_item(attributes)

  index: ->
    @view = new Sprint.Views.Inventories.IndexView(inventories: @inventories)
    $("#inventories").html(@view.render().el)

this is my Inventory view:

   //app/assets/javascripts/backbone/models/inventory.js.coffee

Sprint.Views.Inventories ||= {}

class Sprint.Views.Inventories.InventoryView extends Backbone.View
  template: JST["backbone/templates/inventories/inventory"]

  events:
    "click .destroy" : "destroy"

  initialize: ->
    @listenTo @model, 'change', @render
    @listenTo @model, 'reset', @render

  tagName: "tr"

  destroy: () ->
    @model.destroy()
    this.remove()
    return false
    window.locition.hash = "#/index"

  render: ->
    @$el.html(@template(@model.toJSON() ))
    return this

Solution

  • You have this:

    initialize: () ->
      @options.inventories.bind('reset', @addAll)
      @options.inventories.on('add', @addOne) //not working
    

    that suggests that @options.inventories is a collection. Then later in the same view:

    create_item: (attributes) ->  //this is called from my other models view
      this.collection = new Sprint.Collections.InventoriesCollection 
      this.model = new this.collection.model(item: attributes.name, user_id: router.stats.models[0].attributes.user_id)
      this.collection.create(this.model.toJSON())
    

    you create a new collection, assign it to @collection, and then add your model to @collection. But you're listening to @options.inventories.

    Generally you'd pass the collection to the view as the collection option and Backbone will set @collection on its own:

    constructor / initialize new Backbone.Collection([models], [options])

    [...] There are a couple of options that, if provided, are attached to the collection directly: model and comparator.

    So if you say:

    new V([...], collection: some_collection)
    

    then Backbone will set @collection to be some_collection in your view for you.

    You should also use listenTo instead of on when you can.

    Your view should look more like this:

    initialize: () ->
      @listenTo(@collection, 'reset', @addAll)
      @listenTo(@collection, 'add',   @addOne)
    
    create_item: (attributes) ->
      @collection.create(attributes)
    

    And you'd instantiate it like this:

    new Sprint.Views.Inventories.IndexView(collection: @inventories)
    

    You'll have to apply similar changes everywhere else.

    Also, Backbone view's no longer set @options for you, in version 1.1.0 from October 2013:

    • Backbone Views no longer automatically attach options passed to the constructor as this.options [...] but you can do it yourself if you prefer.

    So you probably need to update your Backbone and patch up other code as well.