绑定骨干模型木偶ItemView控件 - 阻塞.fetch()? [英] Binding a Backbone Model to a Marionette ItemView - blocking .fetch()?

查看:111
本文介绍了绑定骨干模型木偶ItemView控件 - 阻塞.fetch()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个2部分的问题。 1)是否有更好的方式来渲染模型异步一个看法?我目前正在使用Ajax请求的取模型方法(虽然我在动初始化显式调用它)的然后的渲染使用应用程序时,发泄,这将会从解析方法后,模型内部公布的被称为模板视图。很酷,但靠不住? 2)会阻塞方法是有用的,而且这可能吗?

应用程序呈现此页面:

 布局
导航栏
指数

然后取模型,并呈现这样的:

 布局
导航栏
事情
1
某物
somethingelse

但是,如果我不使用发泄触发器,它(果然)呈现:

 布局
导航栏
事情
1
空值
空值

的HTML模板:

 <  - 地区:的NavBar  - >
<脚本类型=文/模板ID =模板导航栏>
   < D​​IV ID =导航栏>
      导航栏
   < / DIV>
< / SCRIPT>< - 查看:IndexView - >
<脚本类型=文/模板ID =模板指数>
   < D​​IV ID =指数>
      指数
   < / DIV>
< / SCRIPT>< - 查看:ThingView - >
<脚本类型=文/模板ID =模板的事>
   < D​​IV ID =东西>
      事< BR />
      <%= ID%GT;< BR />
      <%= valOne%GT;< BR />
      <%= valTwo%GT;< BR />
   < / DIV>
< / SCRIPT><! - 地区 - >
< D​​IV ID =默认区域>
  <! - 布局 - >
  <脚本类型=文/模板ID =模板默认>
     布局
     < D​​IV ID =区域导航栏>
     < / DIV>
     < D​​IV ID =区域的内容>
     < / DIV>
  < / SCRIPT>
< / DIV>

app.js

  window.App = {}#区
类RegionContainer扩展Backbone.Marionette.Region
  EL:#默认区域
  #调用的区域当视图已经被渲染
  昂秀:(视图) - >
    的console.log昂秀RegionContainerApp.RegionContainer = RegionContainer#布局
类DefaultLayout扩展Backbone.Marionette.Layout
  模板:#模板默认
  地区:
    navbarRegion:#区域导航栏
    contentRegion:#地区的内容'
  昂秀:(视图) - >
    的console.log昂秀DefaultLayoutApp.DefaultLayout = DefaultLayout#的NavBar(查看)
一流的NavBar扩展Backbone.Marionette.ItemView
  模板:#模板导航栏
  初始化:() - >
    的console.log为init App.NavBarApp.NavBar =的NavBar#索引视图
类IndexView扩展Backbone.Marionette.ItemView
  模板:#模板指数
  初始化:() - >
    的console.log为init App.IndexViewApp.IndexView = IndexView#查看事
类ThingView扩展Backbone.Marionette.ItemView
  模板:#模板事
  型号:空
  初始化:() - >
    的console.log为init App.ThingView
  事件:
    点击.test_button按钮:DoSomething的
  doSomething的:() - >
    的console.log'ItemView控件的事件 - >做一点事()'App.ThingView = ThingView#型号事情
类东西宽Backbone.Model
  默认值:
    ID:空
    valOne:空
    valTwo:空
  网址:() - GT;
    '/事/'+ @ attributes.id
  初始化:(项目) - >
    的console.log为init App.Thing
    @取()
  解析:(RESP,XHR) - GT;
    的console.log解析响应:'+ JSON.stringify RESP
    #RESP:{ID:1,valOne:东西,valTwo:somethingelse}
    @ attributes.id = resp.id
    @ attributes.valOne = resp.valOne
    @ attributes.valTwo = resp.valTwo
    的console.log的东西:'+ @ JSON.stringify
    @
    App.MyApp.vent.triggerthingisdoneApp.Thing =事情#应用程序
$ - >  #创建应用程序,允许全球访问
  MyApp的=新Backbone.Marionette.Application()
  App.MyApp = MyApp来  #RegionContainer
  regionContainer =新App.RegionContainer  #DefaultLayout
  defaultLayout =新App.DefaultLayout
  regionContainer.show defaultLayout  #查看
  navBarView =新App.NavBar
  indexView =新App.IndexView  #显示默认
  defaultLayout.navbarRegion.show navBarView
  defaultLayout.contentRegion.show indexView   #允许全球访问
  App.defaultRegion = regionContainer
  App.defaultLayout = defaultLayout  #为MyQpp设置默认数据(不能为空?)
  数据=
    说:这个'  #在应用初始化...
  App.MyApp.addInitializer(数据) - >
    的console.log为init App.MyApp    #测试
    App.modelViewTrigger = - >
      的console.log通过模型触发Ajax请求,渲染视图
      App.MyApp.vent.trigger显示:事    App.timeoutInit = - >
      的console.logINIT超时
      setTimeout的'App.modelViewTrigger(),2000    App.timeoutInit()    #事件的pub / sub处理
    App.MyApp.vent.on显示:事', - >
      的console.log收到的消息 - >显示:事
      事情=新App.Thing(ID:1)
      App.thingView =新App.ThingView(型号:物)
      #我应该能够做到这一点,但它呈现空
      #App.defaultLayout.contentRegion.show App.thingView    #测试,看看是否我可以从里面model..yes酒吧!
    App.MyApp.vent.onthingisdone', - >
      的console.log收到的消息 - > thingisdone
      App.defaultLayout.contentRegion.show App.thingView  MyApp.start数据


解决方案

从一个非常基本的角度来看,撇开扔的具体例子,您提供的,这里是我会怎么处理这个问题和解决方案。

的常见问题/解决方案

下面是问题的一个仿制版本:


  • 您需要通过其ID获取的典范。

  • 您需要一个视图来渲染模型被取出后。

这是相当简单的。获取数据前将其模型视图,然后使用该模型的同步事件渲染视图:

  MyView的= Backbone.View.extend({
  初始化:功能(){
    this.model.on(同步,this.render,这一点);
  },  渲染:功能(){...}
});
基于myModel =新为MyModel({ID:someId});
新的MyView的({
  模型:基于myModel
});myModel.fetch();

注意事项:

我打电话型号之前,设置其ID的模型,并与模型视图。这是必要的,以prevent的竞争条件加载数据和渲染视图之间。

我在这里指定的通用骨干的东西。木偶一般工作相同,但为你做的渲染。

您的特定需求

禁止取

坏主意,各地。不要尝试了。

一个阻塞获取将使您的应用程序完全没有响应,直到数据从服务器返回。这将体现自身作为表现不佳,并冻结在用户尝试做任何事情任何时候申请。

的关键不这样做是考虑事件的优点,并确保你的事件被配置你实际上使异步调用之前,如图我的通用示例

和别叫从模型的初始化中获取。这是自找麻烦,你将无法建立取发生之前的任何意见或事件。我是pretty肯定这将解决大部分的你与异步调用有问题。

视图和模型之间的事件

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

在换言之,模型应该直接触发事件和视图应当收听模型上的事件。这部作品以同样的方式作为我的简单的例子,但你可以有你的模型在任何时间触发你想要的任何事件。

我也一定要使用木偶的意见 bindTo 功能,以协助当视图被关闭清理事件。

  MyView的= Backbone.Marionette.ItemView.extend({
  初始化:功能(){
    this.bindTo(this.model,这样做:什么,this.render,这一点);
  }
});为MyModel = Backbone.Model.extend({
  DoSomething的:功能(){
    this.trigger('做的事:东西');
  }
});基于myModel =新为MyModel();
新的MyView的({
  模型:基于myModel
});myModel.doSomething();

其他物品

有,我认为是造成一些问题,或者对奇数领先的情况下,这将导致问题的一些其他物品。

例如,你有太多的domready中发生的事件: $ - >

这并不是说你必须从该事件中被执行了太多code,但你有这个事件中定义了太多code。你不应该做的比这更多的东西:

  $  - >
  App.MyApp.start(数据)

不要在此事件回调定义Marionette.Application的对象,无论是。这应该对自己的定义,这样就可以设置初始化的domready中的回调之外,再与 app.start触发它们()电话。

看看在BBCloneMail示例应用程序的示例上呈现布局,然后加载数据和外部模板填充后的区域:

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

现场应用: http://bbclonemail.heroku.com/


我不认为我直接回答你的问题,你可能想要的方式,但我是presenting的想法应该带你到你所需要的答案。我希望它能帮助最少。 :)

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), then rendering 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?

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:

layout
navbar
thing
1
null
null

The html templates:

<!-- 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

解决方案

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.

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.

I've specified generic Backbone stuff here. Marionette will generally work the same, but do the rendering for you.

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.

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.

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.

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: $ ->

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.

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:

source: https://github.com/derickbailey/bbclonemail

live app: 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. :)

这篇关于绑定骨干模型木偶ItemView控件 - 阻塞.fetch()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆