0

我很困惑 - 我以为我的模型绑定工作正常,但它只是一个带有伪造 ajax 请求的 jsFiddle。我已将模型绑定到视图,如果我覆盖.fetch()并伪造响应,一切正常(我可以更新模型并在页面上更新视图)。但是,当我覆盖.fetch()并使用urlRoot参数并等待响应时,我会收到错误。

.fetch()这是工作的 jsFiddle,在使用伪造的响应调用后呈现模型,而不是更改:

http://jsfiddle.net/franklovecchio/FkNwG/182/

所以如果我在服务器端有一个 API 调用:

/thing/:id

有一个示例响应/thing/1

{"id":"1","latitude":"lat1","longitude":"lon1"} 

我注释掉.fetch(),我得到控制台错误:

load js core functions core.js:2
init model timeout app.js:114
initializer callback for history, routes app.js:95
App.Layouts.MyLayout onShow app.js:41
App.Regions.MyRegion onShow app.js:25
App.Models.Thing init app.js:55
App.ItemViews.Thing init app.js:87
Uncaught TypeError: Object #<Object> has no method 'toJSON' backbone.marionette-0.8.1.min.js:9
Parsing App.Models.Thing.fetch() response: {"id":"1","latitude":"lat1","longitude":"lon1"} app.js:62
Thing: {"id":"1","latitude":"lat1","longitude":"lon1"} app.js:66
a Thing has changed, update ItemView! app.js:57

Uncaught TypeError: Cannot call method 'render' of undefined app.js:58

update model app.js:108

Uncaught TypeError: Object #<Object> has no method 'set' 

.fetch()注释掉的代码:

window.App = { }
window.App.Regions = { } 
window.App.Layouts = { }
window.App.Models = { } 
window.App.ItemViews = { } 
window.App.Rendered = { } 
window.App.Data = { }

# ----------------------------------------------------------------
# App.Regions.MyRegion
# ----------------------------------------------------------------

class MyRegion extends Backbone.Marionette.Region
  el: '#myregion'   
  onShow: (view) ->
    console.log 'App.Regions.MyRegion onShow'

App.Regions.MyRegion = MyRegion

# ----------------------------------------------------------------
# App.Layouts.MyLayout
# ----------------------------------------------------------------

class MyLayout extends Backbone.Marionette.Layout
  template: '#template-mylayout'  
  regions:
    contentRegion: '#content'
    anotherRegion: '#another'
  onShow: (view) ->
    console.log 'App.Layouts.MyLayout onShow'

App.Layouts.MyLayout = MyLayout

# ----------------------------------------------------------------
# App.Models.Thing
# ----------------------------------------------------------------

class Thing extends Backbone.Model

  urlRoot: () ->
    '/thing'

  initialize: (item) ->
    console.log 'App.Models.Thing init'
    @bind 'change', ->
      console.log 'a Thing has changed, update ItemView!'
      @view.render()

  parse: (resp) ->
    console.log 'Parsing App.Models.Thing.fetch() response: ' + JSON.stringify resp
    @attributes.id = resp.id
    @attributes.latitude = resp.latitude
    @attributes.longitude = resp.longitude
    console.log 'Thing: ' + JSON.stringify @
    @

  # If I don't override, I get an error.
  ###fetch: () ->
    console.log 'override ajax for test - App.Models.Thing.fetch()'
    resp =
      id: 1
      latitude: 'lat1'
      longitude: 'lon1'
    console.log 'Faked Thing response: ' + JSON.stringify resp
    @parse resp###

App.Models.Thing = Thing

# ----------------------------------------------------------------
# App.ItemViews.Thing
# ----------------------------------------------------------------

class Thing extends Backbone.Marionette.ItemView
  template: '#template-thing'
  initialize: (options) ->
    console.log 'App.ItemViews.Thing init'
    # Bind
    @options.model.view = @

App.ItemViews.Thing = Thing

# ----------------------------------------------------------------
# App.MyApp ...the Marionette application
# ----------------------------------------------------------------

App.MyApp = new Backbone.Marionette.Application()

# ----------------------------------------------------------------
# App.MyApp before init
# ---------------------------------------------------------------- 

App.MyApp.addInitializer (data) ->
  console.log 'initializer callback for history, routes'

  App.Rendered.myRegion = new App.Regions.MyRegion
  App.Rendered.myLayout = new App.Layouts.MyLayout

  App.Rendered.myRegion.show App.Rendered.myLayout

  # GET thing
  App.Data.thing = new App.Models.Thing(id: 1)
    .fetch()

  App.Rendered.thingView = new App.ItemViews.Thing(model: App.Data.thing)
  App.Rendered.myLayout.contentRegion.show App.Rendered.thingView

# ----------------------------------------------------------------
# Test
# ----------------------------------------------------------------

App.updateModel = ->
  console.log 'update model'

  # Update the Thing with id = 1
  App.Data.thing.set
    latitude: 'somenewlat'

App.updateModelTimeout = ->
  console.log 'init model timeout'
  setTimeout 'App.updateModel()', 2000

App.updateModelTimeout()

$ ->
  data = { }
  App.MyApp.start data

​
4

1 回答 1

5

这里发生了很多奇怪和困惑的事情。不要害怕,一切都还没有丢失,混乱可以解决。

Backbonefetch应该返回 a jqXHR,而不是模型本身。您的fetch实现错误地返回@parse resp并且您的parse返回@(这也是不正确的,有时两个错误确实是正确的)。结果是这样的:

App.Data.thing = new App.Models.Thing(id: 1).fetch()

App.Data.thing当你使用你的时会给你一个有用的,fetch但它不会与 Backbone's 一起使用fetch。所以你fetch的坏了,你没有fetch正确使用;然后您尝试将jqXHR视图作为模型提供给您的视图,并且您的视图设置在模型@viewjqXHR而不是模型上:

initialize: (options) ->
  #...
  @options.model.view = @

因此,您最终得到了一个view属性,jqXHR但模型没有@view(因为App.Data.thing不是模型),并且您在模型的更改处理程序中收到“无法调用未定义的方法'render'”错误。

你应该这样做:

App.Data.thing = new App.Models.Thing(id: 1)
App.Data.thing.fetch()

现在到你的parse

parse: (resp) ->
  console.log 'Parsing App.Models.Thing.fetch() response: ' + JSON.stringify resp
  @attributes.id = resp.id
  @attributes.latitude = resp.latitude
  @attributes.longitude = resp.longitude
  console.log 'Thing: ' + JSON.stringify @
  @

来自精美手册

每当服务器返回模型的数据时,在fetchsave中都会调用parse。该函数传递原始response对象,并应返回要在模型上设置的属性哈希。

所以parse只是应该将服务器的响应按摩成可以set在模型上的东西。您parse正在设置属性并返回@. 结果是你最终会做相当于m.set(m). 所以摆脱你的parse实现,你的是不正确的,你甚至不需要一个。

您的模型/视图连接是向后的:视图参考模型,模型不参考视图。你的模型中有这个:

initialize: (item) ->
  console.log 'App.Models.Thing init'
  @bind 'change', ->
    console.log 'a Thing has changed, update ItemView!'
    @view.render()

在您看来,这是:

initialize: (options) ->
  console.log 'App.ItemViews.Thing init'
  # Bind
  @options.model.view = @

您应该在创建模型时将模型传递给视图(您可以这样做):

App.Rendered.thingView = new App.ItemViews.Thing(model: App.Data.thing)

然后视图应该绑定到模型:

initialize: (options) ->
    @model.on('change', @render)

initialize你可以在你的模型中删除实现。

此外,您可以(并且应该)直接在命名空间中声明类,不要这样做:

class Thing extends Backbone.Marionette.ItemView
  #...
App.ItemViews.Thing = Thing

做这个:

class App.ItemViews.Thing extends Backbone.Marionette.ItemView
  #...

setTimeout此外,几乎不应该使用 is evil的 string/eval 形式。不要这样做:

setTimeout 'App.updateModel()', 2000

做这个:

setTimeout (-> App.updateModel()), 2000

可能还有更多,但希望这能让您开始并解决您的直接问题。

于 2012-05-21T21:14:41.507 回答