Javascript 将主干模型绑定到 Marionette ItemView - 阻止 .fetch()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10643474/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Binding a Backbone Model to a Marionette ItemView - blocking .fetch()?
提问by Derick Bailey
This is a 2 part question. 1) Is there a better way to render a model to a view asynchronously? I'm currently making the ajax request using the fetch
method in the model (though I'm calling it explicitly upon initilization), thenrendering the templated view using an application event, vent
, which gets published from inside the model after the parse
method is called. Cool but wonky? 2) Would a blocking fetch
method be of use, and is it possible?
这是一个两部分的问题。1)有没有更好的方法将模型异步渲染到视图?我目前正在使用fetch
模型中的方法发出 ajax 请求(尽管我在初始化时显式调用它),然后使用应用程序事件渲染模板化视图,vent
在parse
调用方法后从模型内部发布。酷但不稳定?2)阻塞fetch
方法是否有用,是否可能?
The application renders this to the page:
应用程序将其呈现给页面:
layout
navbar
index
Then it fetches the model and renders this:
然后它获取模型并呈现:
layout
navbar
thing
1
something
somethingelse
But, if I don't use the vent
trigger, it (expectedly) renders:
但是,如果我不使用vent
触发器,它(预期)会呈现:
layout
navbar
thing
1
null
null
The html templates:
html模板:
<!-- Region: NavBar -->
<script type="text/template" id="template-navbar">
<div id="navbar">
navbar
</div>
</script>
<!-- View: IndexView -->
<script type="text/template" id="template-index">
<div id="index">
index
</div>
</script>
<!-- View: ThingView -->
<script type="text/template" id="template-thing">
<div id="thing">
thing<br/>
<%= id %><br/>
<%= valOne %><br/>
<%= valTwo %><br/>
</div>
</script>
<!-- Region -->
<div id="default-region">
<!-- Layout -->
<script type="text/template" id="template-default">
layout
<div id="region-navbar">
</div>
<div id="region-content">
</div>
</script>
</div>
app.js:
应用程序.js:
window.App = { }
# Region
class RegionContainer extends Backbone.Marionette.Region
el: '#default-region'
# Called on the region when the view has been rendered
onShow: (view) ->
console.log 'onShow RegionContainer'
App.RegionContainer = RegionContainer
# Layout
class DefaultLayout extends Backbone.Marionette.Layout
template: '#template-default'
regions:
navbarRegion: '#region-navbar'
contentRegion: '#region-content'
onShow: (view) ->
console.log 'onShow DefaultLayout'
App.DefaultLayout = DefaultLayout
# NavBar (View)
class NavBar extends Backbone.Marionette.ItemView
template: '#template-navbar'
initialize: () ->
console.log 'init App.NavBar'
App.NavBar = NavBar
# Index View
class IndexView extends Backbone.Marionette.ItemView
template: '#template-index'
initialize: () ->
console.log 'init App.IndexView'
App.IndexView = IndexView
# Thing View
class ThingView extends Backbone.Marionette.ItemView
template: '#template-thing'
model: null
initialize: () ->
console.log 'init App.ThingView'
events:
'click .test_button button': 'doSomething'
doSomething: () ->
console.log 'ItemView event -> doSomething()'
App.ThingView = ThingView
# Thing Model
class Thing extends Backbone.Model
defaults:
id: null
valOne: null
valTwo: null
url: () ->
'/thing/' + @attributes.id
initialize: (item) ->
console.log 'init App.Thing'
@fetch()
parse: (resp, xhr) ->
console.log 'parse response: ' + JSON.stringify resp
# resp: {"id":"1","valOne":"something","valTwo":"somethingelse"}
@attributes.id = resp.id
@attributes.valOne = resp.valOne
@attributes.valTwo = resp.valTwo
console.log 'Thing: ' + JSON.stringify @
@
App.MyApp.vent.trigger 'thingisdone'
App.Thing = Thing
# App
$ ->
# Create application, allow for global access
MyApp = new Backbone.Marionette.Application()
App.MyApp = MyApp
# RegionContainer
regionContainer = new App.RegionContainer
# DefaultLayout
defaultLayout = new App.DefaultLayout
regionContainer.show defaultLayout
# Views
navBarView = new App.NavBar
indexView = new App.IndexView
# Show defaults
defaultLayout.navbarRegion.show navBarView
defaultLayout.contentRegion.show indexView
# Allow for global access
App.defaultRegion = regionContainer
App.defaultLayout = defaultLayout
# Set default data for MyQpp (can't be empty?)
data =
that: 'this'
# On application init...
App.MyApp.addInitializer (data) ->
console.log 'init App.MyApp'
# Test
App.modelViewTrigger = ->
console.log 'trigger ajax request via model, render view'
App.MyApp.vent.trigger 'show:thing'
App.timeoutInit = ->
console.log 'init timeout'
setTimeout 'App.modelViewTrigger()', 2000
App.timeoutInit()
# Event pub/sub handling
App.MyApp.vent.on 'show:thing', ->
console.log 'received message -> show:thing'
thing = new App.Thing(id: '1')
App.thingView = new App.ThingView(model: thing)
# I should be able to do this, but it renders null
# App.defaultLayout.contentRegion.show App.thingView
# Testing to see if I could pub from inside model..yes!
App.MyApp.vent.on 'thingisdone', ->
console.log 'received message -> thingisdone'
App.defaultLayout.contentRegion.show App.thingView
MyApp.start data
回答by Derick Bailey
From a very basic standpoint, throwing aside the specific example that you've provided, here is how I would approach the problem and solution.
从一个非常基本的角度来看,抛开您提供的具体示例,以下是我将如何处理问题和解决方案。
A Generic Problem / Solution
一般问题/解决方案
Here's a generic version of the problem:
这是问题的通用版本:
- You need to fetch a model by its id.
- You need a view to render after the model has been fetched.
- 您需要通过其 id 获取模型。
- 在获取模型后,您需要一个要渲染的视图。
This is fairly simple. Attach the model to the view before fetching the data, then use the "sync" event of the model to render the view:
这相当简单。在获取数据之前将模型附加到视图,然后使用模型的“同步”事件来渲染视图:
MyView = Backbone.View.extend({
initialize: function(){
this.model.on("sync", this.render, this);
},
render: function(){ ... }
});
myModel = new MyModel({id: someId});
new MyView({
model: myModel
});
myModel.fetch();
Things to note:
注意事项:
I'm setting up the model with its id, and the view with the model before calling fetch
on the model. This is needed in order to prevent a race condition between loading the data and rendering the view.
在调用模型之前,我正在使用其 id 设置模型,并使用模型设置视图fetch
。这是为了防止加载数据和渲染视图之间的竞争条件。
I've specified generic Backbone stuff here. Marionette will generally work the same, but do the rendering for you.
我在这里指定了通用的 Backbone 东西。Marionette 通常会以相同的方式工作,但会为您进行渲染。
Your Specific Needs
您的特定需求
Blocking Fetch
阻塞获取
Bad idea, all around. Don't try it.
坏主意,到处都是。不要尝试。
A blocking fetch will make your application completely unresponsive until the data has returned from the server. This will manifest itself as an application that performs poorly and freezes any time the user tries to do anything.
阻塞获取将使您的应用程序完全无响应,直到数据从服务器返回。这将表现为一个应用程序性能不佳,并且在用户尝试执行任何操作时都会冻结。
The key to not doing this is taking advantage of events and ensuring that your events are configured before you actually make the asynchronous call, as shown in my generic example.
不这样做的关键是利用事件并确保在您实际进行异步调用之前配置您的事件,如我的通用示例所示。
And don't call the fetch from within the model's initializer. That's asking for trouble as you won't be able to set up any views or events before the fetch happens. I'm pretty sure this will solve the majority of the problems you're having with the asynchronous call.
并且不要从模型的初始值设定项中调用 fetch。这会带来麻烦,因为在获取发生之前您将无法设置任何视图或事件。我很确定这将解决您在异步调用中遇到的大多数问题。
Events Between View And Model
视图和模型之间的事件
First, I would avoid using MyApp.vent
to communicate between the model and the view instance. The view already has a reference to the model, so they should communicate directly with each other.
首先,我会避免使用MyApp.vent
模型和视图实例之间的通信。视图已经引用了模型,因此它们应该直接相互通信。
In other words, the model should directly trigger the event and the view should listen to the event on the model. This works in the same way as my simple example, but you can have your model trigger any event you want at any time.
换句话说,模型应该直接触发事件,视图应该监听模型上的事件。这与我的简单示例的工作方式相同,但您可以让模型随时触发您想要的任何事件。
I would also be sure to the use bindTo
feature of Marionette's views, to assist in cleaning up the events when the view is closed.
我还要确保使用bindTo
Marionette 视图的功能,以在视图关闭时协助清理事件。
MyView = Backbone.Marionette.ItemView.extend({
initialize: function(){
this.bindTo(this.model, "do:something", this.render, this);
}
});
MyModel = Backbone.Model.extend({
doSomething: function(){
this.trigger('do:something');
}
});
myModel = new MyModel();
new MyView({
model: myModel
});
myModel.doSomething();
Other Items
其他项目
There are some other items that I think are causing some problems, or leading toward odd situations that will cause problems.
我认为还有一些其他项目会导致一些问题,或者导致会导致问题的奇怪情况。
For example, you have too much happening in the DOMReady event: $ ->
例如,您在 DOMReady 事件中发生了太多事情: $ ->
It's not that you have too much code being executed from this event, but you have too much code defined within this event. You should not have to do anything more than this:
并不是您从该事件中执行了太多代码,而是您在该事件中定义了太多代码。你不应该做比这更多的事情:
$ ->
App.MyApp.start(data)
Don't define your Marionette.Application object in this event callback, either. This should be defined on its own, so that you can set up your initializers outside of the DOMReady callback, and then trigger them with the app.start()
call.
也不要在此事件回调中定义您的 Marionette.Application 对象。这应该自己定义,以便您可以在 DOMReady 回调之外设置初始化程序,然后通过app.start()
调用触发它们。
Take a look at the BBCloneMail sample application for an example on rendering a layout and then populating its regions after loading data and external templates:
查看 BBCloneMail 示例应用程序,了解如何渲染布局,然后在加载数据和外部模板后填充其区域:
source: https://github.com/derickbailey/bbclonemail
来源:https: //github.com/derickbailey/bbclonemail
live app: http://bbclonemail.heroku.com/
实时应用程序:http: //bbclonemail.heroku.com/
I don't think I'm directly answering your questions the way you might want, but the ideas that I'm presenting should lead you to the answer that you need. I hope it helps at least. :)
我认为我并没有按照您可能想要的方式直接回答您的问题,但是我提出的想法应该会引导您找到您需要的答案。我希望它至少有所帮助。:)
回答by Elliot Brightside
See Derick's new suggestion to tackle this common problem at: https://github.com/marionettejs/backbone.marionette/blob/master/upgradeGuide.md#marionetteasync-is-no-longer-supported
请参阅 Derick 解决此常见问题的新建议:https: //github.com/marionettejs/backbone.marionette/blob/master/upgradeGuide.md#marionetteasync-is-no-longer-supported
In short, move the asynchronous code away from your views, which means you need to provide them with models whose data has already been fetched. From the example in Marionette's upgrade guide:
简而言之,将异步代码从您的视图中移开,这意味着您需要为它们提供已获取数据的模型。来自 Marionette 升级指南中的示例:
Marionette.Controller.extend({
showById: function(id){
var model = new MyModel({
id: id
});
var promise = model.fetch();
$.when(promise).then(_.bind(this.showIt, this));
},
showIt: function(model){
var view = new MyView({
model: model
});
MyApp.myRegion.show(view);
}
});