将主干模型绑定到 Marionette ItemView - 阻止 .fetch()? [英] Binding a Backbone Model to a Marionette ItemView - blocking .fetch()?

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

问题描述

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

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

布局导航栏指数

然后它获取模型并渲染:

布局导航栏事物1某物别的东西

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

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

html 模板:

<script type="text/template" id="template-navbar"><div id="导航栏">导航栏

<!-- 视图:IndexView --><script type="text/template" id="template-index"><div id="索引">指数

<!-- 视图:ThingView --><script type="text/template" id="template-thing"><div id="东西">东西<br/><%=id%><br/><%=valOne%><br/><%=valTwo%><br/>

<!-- 区域--><div id="默认区域"><!-- 布局--><script type="text/template" id="template-default">布局<div id="region-navbar">

<div id="区域内容">

app.js:

window.App = { }# 地区类 RegionContainer 扩展了 Backbone.Marionette.Regionel: '#default-region'# 当视图被渲染时在区域上调用onShow:(查看)->console.log 'onShow RegionContainer'App.RegionContainer = RegionContainer# 布局类 DefaultLayout 扩展了 Backbone.Marionette.Layout模板:'#template-default'地区:navbarRegion: '#region-navbar'contentRegion: '#region-content'onShow:(查看)->console.log 'onShow DefaultLayout'App.DefaultLayout = DefaultLayout# 导航栏(视图)类 NavBar 扩展了 Backbone.Marionette.ItemView模板:'#template-navbar'初始化:() ->console.log 'init App.NavBar'App.NavBar = 导航栏# 索引视图类 IndexView 扩展了 Backbone.Marionette.ItemView模板:'#template-index'初始化:() ->console.log 'init App.IndexView'App.IndexView = 索引视图# 事物视图类 ThingView 扩展了 Backbone.Marionette.ItemView模板:'#template-thing'型号:空初始化:() ->console.log 'init App.ThingView'事件:'点击 .test_button 按钮':'doSomething'doSomething: () ->console.log 'ItemView 事件 ->做一点事()'App.ThingView = ThingView# 事物模型类 Thing 扩展了 Backbone.Model默认值:id:空值一:空值二:空网址:() ->'/东西/' + @attributes.id初始化:(项目)->console.log 'init App.Thing'@拿来()解析: (resp, xhr) ->console.log '解析响应:' + JSON.stringify 响应# resp: {"id":"1","valOne":"something","valTwo":"somethingelse"}@attributes.id = resp.id@attributes.valOne = resp.valOne@attributes.valTwo = resp.valTwoconsole.log '东西:' + JSON.stringify @@App.MyApp.vent.trigger 'thingisdone'App.Thing = 事物# 应用程序$ -># 创建应用程序,允许全局访问MyApp = new Backbone.Marionette.Application()App.MyApp = MyApp# 区域容器regionContainer = 新 App.RegionContainer# 默认布局defaultLayout = 新 App.DefaultLayoutregionContainer.show defaultLayout# 视图navBarView = 新 App.NavBarindexView = 新 App.IndexView# 显示默认值defaultLayout.navbarRegion.show navBarViewdefaultLayout.contentRegion.show indexView# 允许全局访问App.defaultRegion = regionContainerApp.defaultLayout = defaultLayout# 设置MyQpp的默认数据(不能为空?)数据 =那:'这个'# 在应用程序初始化...App.MyApp.addInitializer(数据)->console.log 'init App.MyApp'# 测试App.modelViewTrigger = ->console.log '通过模型触发 ajax 请求,呈现视图'App.MyApp.vent.trigger 'show:thing'App.timeoutInit = ->console.log '初始化超时'setTimeout 'App.modelViewTrigger()', 2000App.timeoutInit()# 事件发布/订阅处理App.MyApp.vent.on 'show:thing', ->console.log '收到消息 ->显示:东西'thing = new App.Thing(id: '1')App.thingView = new App.ThingView(model: thing)# 我应该能够做到这一点,但它呈现为空# App.defaultLayout.contentRegion.show App.thingView# 测试我是否可以从模型内部发布..是的!App.MyApp.vent.on 'thingisdone', ->console.log '收到消息 ->事情已经完成'App.defaultLayout.contentRegion.show App.thingViewMyApp.start 数据

解决方案

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

一般问题/解决方案

这是问题的通用版本:

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

MyView = Backbone.View.extend({初始化:函数(){this.model.on("sync", this.render, this);},渲染:函数(){ ... }});myModel = new MyModel({id: someId});新的我的视图({型号:myModel});myModel.fetch();

注意事项:

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

我在这里指定了通用的 Backbone 内容.Marionette 通常会以相同的方式工作,但会为您进行渲染.

您的特定需求

阻止获取

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

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

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

并且不要从模型的初始值设定项中调用提取.这会带来麻烦,因为在获取发生之前您将无法设置任何视图或事件.我很确定这将解决您在异步调用中遇到的大部分问题.

视图和模型之间的事件

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

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

我还要确保使用 Marionette 视图的 bindTo 功能,以在视图关闭时协助清理事件.

MyView = Backbone.Marionette.ItemView.extend({初始化:函数(){this.bindTo(this.model, "do:something", this.render, this);}});MyModel = Backbone.Model.extend({做某事:功能(){this.trigger('do:something');}});myModel = new MyModel();新的我的视图({型号:myModel});myModel.doSomething();

其他项目

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

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

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

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

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

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

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

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

<小时>

我认为我没有按照您希望的方式直接回答您的问题,但我提出的想法应该会引导您找到所需的答案.我希望它至少有帮助.:)

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:

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

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

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆