【问题标题】:Overriding .fetch() works with fake data, errors with urlRoot and actual Model ajax request覆盖 .fetch() 适用于虚假数据、urlRoot 错误和实际模型 ajax 请求
【发布时间】:2012-05-28 08:14:54
【问题描述】:

我很困惑 - 我以为我的模型绑定工作正常,但它只是作为带有伪造 ajax 请求的 jsFiddle。我已将模型绑定到视图,如果我覆盖 .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

​

【问题讨论】:

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


    【解决方案1】:

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

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

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

    在您使用 fetch 时为您提供有用的 App.Data.thing,但与 Backbone 的 fetch 不匹配。所以你的fetch 坏了,你没有正确使用fetch;然后您尝试将jqXHR 作为模型提供给您的视图,并且您的视图将@view 设置为jqXHR 而不是模型:

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

    所以你最终在jqXHR 上得到了一个view 属性,但模型没有@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 @
      @
    

    来自fine manual

    parse 在服务器返回模型数据时调用,在 fetchsave 中。该函数传递了原始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 的 string/eval 形式是 evil,几乎不应该使用。不要这样做:

    setTimeout 'App.updateModel()', 2000
    

    这样做:

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

    可能还有更多,但希望这能帮助您开始并解决您眼前的问题。

    【讨论】:

    • 乐于助人,措辞好,有趣。谢谢!
    猜你喜欢
    • 2020-10-27
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    • 2018-08-06
    • 1970-01-01
    相关资源
    最近更新 更多