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

0 javascript model-view-controller coffeescript backbone.js marionette

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

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

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

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

/thing/:id

通过示例响应/thing/1:

{"id":"1","latitude":"lat1","longitude":"lon1"} 
Run Code Online (Sandbox Code Playgroud)

我注释掉了.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' 
Run Code Online (Sandbox Code Playgroud)

.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

?
Run Code Online (Sandbox Code Playgroud)

mu *_*ort 5

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

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

App.Data.thing = new App.Models.Thing(id: 1).fetch()
Run Code Online (Sandbox Code Playgroud)

App.Data.thing当你使用你的时候会给你一个有用的东西,fetch但是对于Backbone来说它是不对的fetch.所以你fetch的破坏了,你没有fetch正确使用; 那么你试着把jqXHR你的视图作为模型,你的视图设置@viewjqXHR而不是模型上:

initialize: (options) ->
  #...
  @options.model.view = @
Run Code Online (Sandbox Code Playgroud)

所以你最终得到了一个view属性,jqXHR但模型没有@view(因为App.Data.thing不是模型),你得到一个"无法调用方法'渲染'未定义"错误在你的模型的变更处理程序中.

你应该这样做:

App.Data.thing = new App.Models.Thing(id: 1)
App.Data.thing.fetch()
Run Code Online (Sandbox Code Playgroud)

现在你的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 @
  @
Run Code Online (Sandbox Code Playgroud)

精细手册:

只要服务器返回模型的数据,在获取保存时,就会调用parse.该函数传递给原始response对象,并应返回要在模型上设置的属性hash .

因此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()
Run Code Online (Sandbox Code Playgroud)

这在你看来:

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

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

App.Rendered.thingView = new App.ItemViews.Thing(model: App.Data.thing)
Run Code Online (Sandbox Code Playgroud)

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

initialize: (options) ->
    @model.on('change', @render)
Run Code Online (Sandbox Code Playgroud)

您可以删除initialize模型中的实现.

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

class Thing extends Backbone.Marionette.ItemView
  #...
App.ItemViews.Thing = Thing
Run Code Online (Sandbox Code Playgroud)

做这个:

class App.ItemViews.Thing extends Backbone.Marionette.ItemView
  #...
Run Code Online (Sandbox Code Playgroud)

此外,setTimeout几乎永远不会使用字符串/评估形式的邪恶.不要这样做:

setTimeout 'App.updateModel()', 2000
Run Code Online (Sandbox Code Playgroud)

做这个:

setTimeout (-> App.updateModel()), 2000
Run Code Online (Sandbox Code Playgroud)

可能会有更多,但希望这将使您开始并解决您的直接问题.