【问题标题】:Binding a Backbone Model to a Marionette ItemView - blocking .fetch()?将主干模型绑定到 Marionette ItemView - 阻塞 .fetch()?
【发布时间】:2012-05-17 21:02:25
【问题描述】:

这是一个两部分的问题。 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>

app.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

【问题讨论】:

    标签: javascript backbone.js marionette


    【解决方案1】:

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

    一般问题/解决方案

    这是问题的一般版本:

    • 您需要通过 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();
    

    注意事项:

    在模型上调用fetch 之前,我正在设置模型及其ID,以及模型视图。这是为了防止加载数据和渲染视图之间的竞争条件。

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

    您的具体需求

    阻止抓取

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

    阻塞获取将使您的应用程序在数据从服务器返回之前完全无响应。这将表现为应用程序性能不佳,并且在用户尝试执行任何操作时都会冻结。

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

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

    视图和模型之间的事件

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

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

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

    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 事件中发生了太多事情:$ -&gt;

    并不是你从这个事件中执行了太多的代码,而是你在这个事件中定义了太多的代码。除此以外,您无需执行任何操作:

    $ -> 
      App.MyApp.start(data)
    

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

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

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

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


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

    【讨论】:

    • 在弄乱了一些代码之后,我想我开始明白你的意思了。这个需要坐一会儿 :) 谢谢!
    • @Derick-bailey,如何防止 Marionette 在获取模型之前渲染视图?由于模型已经包含一个 id,Marionette 会尝试立即渲染它吗?
    • Hey Derick — 你还会认为这是构建 fetch + 渲染的最新最佳实践吗?很好奇,因为我在我的应用程序中尝试了几种不同的方法,并且很好奇 Derick Approved®.. ;)
    • 我会记下此回复的日期。特别是,在 Marionette v1.0.0-rc3 中,bindTo 被替换为 listenTo 方法。 github.com/marionettejs/backbone.marionette/blob/master/…
    【解决方案2】:

    查看 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);
      }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多