11

これは2部構成の質問です。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
4

2 に答える 2

36

非常に基本的な観点から、あなたが提供した特定の例は別として、ここに私が問題と解決策に取り組む方法があります。

一般的な問題/解決策

問題の一般的なバージョンは次のとおりです。

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

注意事項:

モデルを呼び出す前に、IDを使用してモデルを設定し、モデルを使用してビューを設定していますfetch。これは、データのロードとビューのレンダリングの間の競合状態を防ぐために必要です。

ここでは、一般的なバックボーンのものを指定しました。マリオネットは通常同じように機能しますが、レンダリングを行います。

あなたの特定のニーズ

ブロッキングフェッチ

悪い考えだ。試さないでください。

フェッチをブロックすると、サーバーからデータが返されるまで、アプリケーションが完全に応答しなくなります。これは、パフォーマンスが低く、ユーザーが何かをしようとするとフリーズするアプリケーションとして現れます。

これを行わないための鍵は、私の一般的な例に示すように、イベントを利用し、実際に非同期呼び出しを行う前にイベントが構成されていることを確認することです。

また、モデルの初期化子内からフェッチを呼び出さないでください。フェッチが発生する前にビューやイベントを設定できないため、問題が発生します。これにより、非同期呼び出しで発生する問題の大部分が解決されると確信しています。

ビューとモデルの間のイベント

MyApp.ventまず、モデルとビューインスタンス間の通信にを使用することは避けます。ビューにはすでにモデルへの参照があるため、ビューは相互に直接通信する必要があります。

つまり、モデルはイベントを直接トリガーする必要があり、ビューはモデル上のイベントをリッスンする必要があります。これは私の単純な例と同じように機能しますが、モデルにいつでも必要なイベントをトリガーさせることができます。

また、マリオネットのビューの機能を使用して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イベントで発生していることが多すぎます。$ ->

このイベントから実行されているコードが多すぎるということではありませんが、このイベント内で定義されているコードが多すぎます。これ以上のことをする必要はありません:

$ -> 
  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.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 に答える