重写.fetch()适用于伪数据,urlRoot错误和实际的Model ajax请求

时间:2012-05-21 20:06:09

标签: javascript model-view-controller backbone.js coffeescript marionette

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

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

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

​

1 个答案:

答案 0 :(得分:5)

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

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

App.Data.thing = new App.Models.Thing(id: 1).fetch()
当您使用App.Data.thing时,

会为您提供有用的fetch,但使用Backbone的fetch时却不对。因此,fetch已被破坏,而您未正确使用fetch;然后,您尝试将jqXHR作为模型提供给您的视图,并在@view而不是模型上设置jqXHR视图:

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

所以你最终得到view上的jqXHR属性,但模型没有@view(因为App.Data.thing不是模型)而且你得到了“不能调用方法'渲染'未定义“模型的更改处理程序中的错误。

你应该这样做:

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 @
  @

来自fine manual

  只要服务器返回模型的数据, fetch save ,就会调用解析。该函数传递原始response对象,并应在模型上返回属性hash set

因此,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的字符串/ eval形式是邪恶的,几乎不应该使用。不要这样做:

setTimeout 'App.updateModel()', 2000

这样做:

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

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