ModelBinder in Coffeescript

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.modelbinder/0.1.3/Backbone.ModelBinder-min.js"></script>
<script src="https://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.CollectionBinder.min.js"></script>
This is a simple example that shows how to use the ModelBinding with coffeescript.  Futher, it shows how to update one data attribute in the model in multiple ways. In this example, changes to both sales and sales per square foot are stored in the sales attribute.<br/>
<br/>
Note, for a table with multiple rows, the CollectionBinder can efficently handle the same bindings for multiple rows.
<hr>
<div id="viewContent">

</div>

<script id="my-template" type="text/x-underscore-template">
    <table id="sales_table"  border="1" cellspacing="1" cellpadding="1" summary="table">
      <thead>
      <tr><th COLSPAN=4>Sales Table</th>
      <tr>
        <th>Year</th>
        <th>Sales</th>
        <th>Sales/PSF</th>
        <th>GSF</th>
      </tr></thead>
      <tbody>
        <tr>
            <td name="year"></td>
            <td><input type="text" name="sales"/></td>
            <td><input type="text" name="sales_psf"/></td>
            <td><input type="text" name="gsf"/></td>
        </tr>
      </tbody>
    </table>
</script>

CSS

#sales_table td { text-align: center }
#sales_table th { text-align: center }
#sales_table td input { text-align: center }

CoffeeScript

$.app = $.app || {}
$.app.MyView = $.app.MyView || {}

$ ->
  class MyObject extends Backbone.Model
    defaults:
      year: 1
      sales: 2000
      gsf: 1000
      
    # Don't allow GSF < 1 to bypass division by zero errors.
    setGSF: (gsf) ->
      if gsf >= 1000
        @set('gsf',gsf)
        return gsf
      else
        @set('gsf',1000)
        return 1000
      
        
  class MyView extends Backbone.View
    el: '#viewContent'
    template: _.template($('#my-template').html())
    
    initialize: ->
      @modelBinder = new Backbone.ModelBinder()
      @render
        
    close: ->
      @modelBinder.unbind()
        
    gsfConverter: (direction, value, attribute, model) =>
      if direction == 'ViewToModel'
        value = model.setGSF(value)
        # Since Sales hasn't changed, but Sales/PSF needs to be re-calculated,
        # we manually trigger the attribute binding, as per the discussion:
        # https://github.com/theironcook/Backbone.ModelBinder/issues/36
        @modelBinder._copyModelToView(@modelBinder._attributeBindings['sales'])
        return value
      else
        return value
        
    salesConverter: (direction, value, attribute, model) ->
      if direction == 'ViewToModel'
        return value
      else
        return value
        
    salesPSFConverter: (direction, value, attribute, model) ->
      if direction == 'ViewToModel'
        return value*model.get('gsf')
      else
        return value/model.get('gsf')
        
    render: ->
      $(@el).empty()
      $(@el).html @template(@model.toJSON())
        
      bindings =
        year: '[name=year]'
        gsf: {selector:'[name=gsf]', converter: @gsfConverter},
        sales: [{selector:'[name=sales]', converter: @salesConverter},
        {selector:'[name=sales_psf]', converter: @salesPSFConverter}]
        
      @modelBinder.bind(@model, $(@el), bindings)
 
  $.extend $.app.MyView, new MyView {model: new MyObject}
  $.app.MyView.render()