javascript - 重写 .fetch() 可处理虚假数据、urlRoot 错误和实际模型 ajax 请求

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

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

这是工作中的 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

​

最佳答案

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

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 ,并且你的 View 在jqXHR而不是模型上设置@view:

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: (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 is called whenever a model's data is returned by the server, in fetch, and save. The function is passed the raw response object, and should return the attributes hash to be set on the model.

因此 parse 只是应该将服务器的响应调整为 set在模型上。您的 parse 正在设置属性并返回 @。结果是您最终将执行与 m.set(m) 相同的操作。因此,摆脱您的 parse 实现,您的实现是不正确的,您甚至不需要它。

您的模型/ View 连接是向后的: View 引用模型,模型不引用 View 。你的模型中有这个:

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

您应该在创建模型时将模型传递给 View (您就是这样,所以这没问题):

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

然后 View 应该绑定(bind)到模型:

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

setTimeout 'App.updateModel()', 2000

这样做:

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

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

关于javascript - 重写 .fetch() 可处理虚假数据、urlRoot 错误和实际模型 ajax 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10691860/

相关文章:

javascript - Chrome v71 anchor 标签范围链引用内部 img 标签的范围

java - 从spring mvc中的ajax请求中检索包含名称值对的数组

jquery - $(element).find (".class").css(...) 和 $(element).find (".class").each() 函数中的 $(this).css 之间有区别吗?

javascript - 如何在 youtube 上自动更改字幕?⁣

javascript - 动画背景颜色从/到透明

c# - EXCEPT 运算符是否可以排除列包含数组中数据的数据

jquery - JavaScript MVC 框架

javascript - Backbone : Rendering different ItemViews based on model value

javascript - 使用 jquery 的 when 进行多个 ajax 调用的回调 _.each

javascript - 为什么 JavaScript 不获取整个输入框?