简体   繁体   中英

CoffeeScript/Backbone/Marionette - tutorial example conversion and scoping issues

I'm trying to create a basic version of this example in CoffeeScript: http://davidsulc.com/blog/2012/04/15/a-simple-backbone-marionette-tutorial/ . All the dependencies are declared as .coffee files in the order I write them (in the body), and jQuery + Underscore + Backbone + Marionette are in the head.

I have a model, thing.coffee

$ ->

  console.log 'load model'

  class Thing extends Backbone.Model

    defaults:
      myValue: null

   initialize: ->
     console.log 'new thing'

  window.Thing = Thing

My first hiccup is with scoping: if I don't declare window.Thing , the next file (the collection of Things , can't find the model Thing , and so on. What am I doing wrong there?

I have a collection, things.coffee

$ ->

  console.log 'load collection'

  class Things extends Backbone.Collection

    model: Thing

  window.Things = Things

I have a Marionette view, thing_view.coffee

$ ->

  console.log 'load composite model view'

  ThingView = Backbone.Marionette.ItemView.extend(
    template: '#template-thing'
    tagName: 'tr'
  )

  window.ThingView = ThingView

I have a Marionette view, things_view.coffee

$ ->

  console.log 'load composite collection view'

  ThingsView = Backbone.Marionette.CompositeView.extend(
    tagName:  'table'
    id: 'things'
    template: '#template-things'
    itemView: ThingView
    appendHtml: (collectionView, itemView) ->
      collectionView.$('tbody').append itemView.el
  )

  window.ThingsView = ThingsView

I have an application myapp.coffee

$ ->

  console.log 'load app'

  # Load default data
  thingsCollection = new Things([ 
    new Thing(myValue: 'uno'), 
    new Thing(myValue: 'dos')
  ])

  data:
    thingsCollection: thingsCollection

  # Create application, specify default layouts
  MyApp = new Backbone.Marionette.Application()
  MyApp.addRegions singleRegion: '#content'

  # On application init...
  MyApp.addInitializer (data) ->
    console.log 'Init application...'
    thingsView = new ThingsView(collection: data.thingsCollection)
    MyApp.singleRegion.show data.thingsView

  # Start application
  MyApp.start data

My html file looks like:

<div id="content">
    <script type="text/template" id="template-things">
       <thead>
          <tr class='header'>
             <th>myValue</th>
          </tr>
      </thead>
      <tbody>
      </tbody>
    </script>
    <script type="text/template" id="template-thing">
       <td><%= myValue %></td>
    </script>
</div>

Following the console.log :

load model
load collection
load composite model view
composite collection view
load app
Init application...
Uncaught TypeError: Cannot call method 'render' of undefined

So, needless to say I'm confused as to what is going on - I'm sure theres tons of things wrong here, so please help!

The window.Thing thing is a byproduct of Coffeescript's global namespace pollution avoidance. The idiomatic way of getting around that is to use an App namespace .

Initialising your classes only on DOM load is a Backbone anti-pattern . If you namespace your classes, then you just have to ensure you load them in the right order (item view before collection view) and they can be created immediately instead of after the page has loaded.

Can you give us an idea of where the Uncaught TypeError is happening?

if I don't declare window.Thing , the next file (the collection of Things , can't find the model Thing , and so on. What am I doing wrong there?

The CoffeeScript compiler wraps each file in a self-executing function so Thing is local to the wrapper unless you forcibly globalize it by putting it in window .

I think your real problem is right here:

MyApp.addInitializer (data) ->
  console.log 'Init application...'
  thingsView = new ThingsView(collection: data.thingsCollection)
  MyApp.singleRegion.show data.thingsView

You create a ThingsView and store it in thingsView . But then you try to pass data.thingsView to MyApp.singleRegion.show but there's nothing in data.thingsView so you get a complaint when something inside Marionette tries to view.render() . I think you want this:

  thingsView = new ThingsView(collection: data.thingsCollection)
  MyApp.singleRegion.show thingsView

or this:

  data.thingsView = new ThingsView(collection: data.thingsCollection)
  MyApp.singleRegion.show data.thingsView

depending on what this data object is all about.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM