A substituição de .fetch() funciona com dados falsos, erros com urlRoot e solicitação real do modelo ajax
-
12-12-2019 - |
Pergunta
Estou confuso - pensei que a vinculação de modelo funcionasse corretamente, mas era apenas como um jsFiddle com solicitação de ajax falsa.Vinculei um modelo a uma visualização e, se eu substituir .fetch()
e falsifique a resposta, tudo funciona (posso atualizar o modelo e a visualização é atualizada na página).No entanto, quando eu não sobrepor .fetch()
e use o urlRoot
param e espero pela resposta, recebo erros.
Aqui está o jsFiddle funcional onde um modelo é renderizado após a chamada .fetch()
com uma resposta falsa, que mudou:
http://jsfiddle.net/franklovecchio/FkNwG/182/
Então, se eu tiver uma chamada de API no lado do servidor:
/thing/:id
Com um exemplo de resposta para /thing/1
:
{"id":"1","latitude":"lat1","longitude":"lon1"}
E eu comento .fetch()
, recebo os erros do console:
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'
O código com .fetch()
comentado:
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
Solução
Há muitas coisas estranhas e confusas acontecendo aqui.Não tenha medo, nem tudo está perdido e a confusão pode ser resolvida.
Espinha dorsal fetch
deveria retornar um jqXHR
, não o modelo em si.Seu fetch
implementação retorna incorretamente @parse resp
e seu parse
retorna @
(o que também é incorreto e às vezes dois erros fazem um acerto).O resultado é que isto:
App.Data.thing = new App.Models.Thing(id: 1).fetch()
dá-lhe um útil App.Data.thing
quando você usa seu fetch
mas não vai dar certo com o Backbone fetch
.Então seu fetch
está quebrado e você não está usando o fetch
corretamente;então você tenta dar o jqXHR
para sua visualização como modelo e seus conjuntos de visualização @view
no jqXHR
em vez do modelo:
initialize: (options) ->
#...
@options.model.view = @
Então você acaba com um view
propriedade no jqXHR
mas o modelo não tem @view
(porque App.Data.thing
não é o modelo) e você recebe um erro "Não é possível chamar o método 'renderizar' de indefinido" no manipulador de alterações do seu modelo.
Você deveria estar fazendo isso:
App.Data.thing = new App.Models.Thing(id: 1)
App.Data.thing.fetch()
Agora vamos para o seu 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 @
@
De bom manual:
analisar é chamado sempre que os dados de um modelo são retornados pelo servidor, em buscar, e salvar.A função é passada em bruto
response
objeto e deve retornar o hash dos atributos a serem definir no modelo.
Então parse
é apenas suposto transformar a resposta do servidor em algo que pode ser set
no modelo.Seu parse
está configurando os atributos e retornando @
.O resultado é que você acabará fazendo o equivalente a m.set(m)
.Então livre-se do seu parse
implementação, a sua está incorreta e você nem precisa de uma.
Sua conexão de modelo/visualização está invertida:visualizações fazem referência a modelos, modelos não fazem referência a visualizações.Você tem isso no seu modelo:
initialize: (item) ->
console.log 'App.Models.Thing init'
@bind 'change', ->
console.log 'a Thing has changed, update ItemView!'
@view.render()
e isso na sua opinião:
initialize: (options) ->
console.log 'App.ItemViews.Thing init'
# Bind
@options.model.view = @
Você deve passar o modelo para a visualização ao criá-lo (o que você está fazendo, então está tudo bem):
App.Rendered.thingView = new App.ItemViews.Thing(model: App.Data.thing)
e então a visão deve ser vinculada ao modelo:
initialize: (options) ->
@model.on('change', @render)
e você pode largar o initialize
implementação em seu modelo.
Além disso, você pode (e deve) declarar classes diretamente em um namespace, não faça isso:
class Thing extends Backbone.Marionette.ItemView
#...
App.ItemViews.Thing = Thing
fazem isto:
class App.ItemViews.Thing extends Backbone.Marionette.ItemView
#...
Além disso, a forma string/eval de setTimeout
é mau quase nunca deve ser usado.Não faça isso:
setTimeout 'App.updateModel()', 2000
fazem isto:
setTimeout (-> App.updateModel()), 2000
Pode haver mais, mas espero que isso ajude você a começar e resolver seus problemas imediatos.