将Backbone Model绑定到Marionette ItemView - 阻止.fetch()?

11 javascript backbone.js marionette

这是一个2部分的问题.1)是否有更好的方法将模型异步渲染到视图中?我正在使用fetch模型中的方法创建ajax请求(虽然我在启动时显式调用它),然后使用应用程序事件渲染模板化视图vent,该parse方法在调用方法后从模型内部发布.很酷但很不稳定?2)阻塞fetch方法是否可以使用,是否可能?

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

layout
navbar
index
Run Code Online (Sandbox Code Playgroud)

然后它获取模型并呈现:

layout
navbar
thing
1
something
somethingelse
Run Code Online (Sandbox Code Playgroud)

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

layout
navbar
thing
1
null
null
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Der*_*ley 35

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

一般问题/解决方案

这是问题的通用版本:

  • 您需要通过其ID获取模型.
  • 在获取模型后,您需要一个视图来渲染.

这很简单.在获取数据之前将模型附加到视图,然后使用模型的"sync"事件来呈现视图:

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();
Run Code Online (Sandbox Code Playgroud)

注意事项:

我在调用模型之前使用其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();
Run Code Online (Sandbox Code Playgroud)

其他项目

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

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

这并不意味着您从此事件中执行了太多代码,但是您在此事件中定义了太多代码.你不应该做更多的事情:

$ -> 
  App.MyApp.start(data)
Run Code Online (Sandbox Code Playgroud)

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

查看BBCloneMail示例应用程序,获取有关渲染布局的示例,然后在加载数据和外部模板后填充其区域:

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

实时应用:http://bbclonemail.heroku.com/


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

  • @ Derick-bailey,你如何阻止Marionette在获取模型之前呈现视图?由于模型已经包含一个id,Marionette会立即尝试渲染它吗? (3认同)

Ell*_*ide 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);
  }
});
Run Code Online (Sandbox Code Playgroud)