11

这是一个两部分的问题。1)有没有更好的方法将模型异步渲染到视图?我目前正在使用fetch模型中的方法发出 ajax 请求(尽管我在初始化时显式调用它),然后使用应用程序事件呈现模板化视图,该事件在调用方法vent后从模型内部发布parse。酷但不稳定?2)阻塞fetch方法有用吗,有可能吗?

应用程序将其呈现到页面:

layout
navbar
index

然后它获取模型并呈现:

layout
navbar
thing
1
something
somethingelse

但是,如果我不使用vent触发器,它(预期)会呈现:

layout
navbar
thing
1
null
null

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>

应用程序.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
4

2 回答 2

36

从一个非常基本的角度来看,抛开您提供的具体示例,这就是我将如何处理问题和解决方案。

一个普遍的问题/解决方案

这是问题的通用版本:

  • 您需要通过其 id 获取模型。
  • 在获取模型后,您需要一个视图来呈现。

这相当简单。在获取数据之前将模型附加到视图,然后使用模型的“同步”事件来渲染视图:

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();

注意事项:

在调用模型之前,我正在使用它的 id 设置模型,并使用模型设置视图fetch。这是为了防止加载数据和渲染视图之间的竞争条件。

我在这里指定了通用的 Backbone 东西。Marionette 的工作原理通常相同,但会为您进行渲染。

您的具体需求

阻止获取

坏主意,到处都是。不要尝试。

在数据从服务器返回之前,阻塞获取将使您的应用程序完全无响应。这将表现为一个性能不佳的应用程序,并在用户尝试做任何事情时冻结。

不这样做的关键是利用事件并确保在实际进行异步调用之前配置事件,如我的通用示例所示。

并且不要从模型的初始化程序中调用 fetch。这是自找麻烦,因为在获取发生之前您将无法设置任何视图或事件。我很确定这将解决您在使用异步调用时遇到的大部分问题。

视图和模型之间的事件

首先,我会避免使用MyApp.vent模型和视图实例之间的通信。视图已经引用了模型,因此它们应该直接相互通信。

换句话说,模型应该直接触发事件,视图应该监听模型上的事件。这与我的简单示例的工作方式相同,但您可以让您的模型随时触发您想要的任何事件。

我也一定会使用bindToMarionette 的视图功能,以在视图关闭时协助清理事件。

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();

其他项目

我认为还有一些其他的项目会导致一些问题,或者导致会导致问题的奇怪情况。

例如,您在 DOMReady 事件中发生了太多事情:$ ->

并不是你从这个事件中执行了太多的代码,而是你在这个事件中定义了太多的代码。你不应该做更多的事情:

$ -> 
  App.MyApp.start(data)

也不要在此事件回调中定义您的 Marionette.Application 对象。这应该自行定义,以便您可以在 DOMReady 回调之外设置初始化程序,然后通过app.start()调用触发它们。

查看 BBCloneMail 示例应用程序,了解有关呈现布局并在加载数据和外部模板后填充其区域的示例:

来源:https ://github.com/derickbailey/bbclonemail

直播应用:http ://bbclonemail.heroku.com/


我认为我不会以您可能想要的方式直接回答您的问题,但我提出的想法应该会引导您找到您需要的答案。我希望它至少有帮助。:)

于 2012-05-18T01:43:18.787 回答
7

请参阅 Derick 解决此常见问题的新建议:https ://github.com/marionettejs/backbone.marionette/blob/master/upgradeGuide.md#marionetteasync-is-no-longer-supported

简而言之,将异步代码从您的视图中移开,这意味着您需要为它们提供已获取数据的模型。来自 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);
  }
});
于 2013-08-01T09:05:49.880 回答