Подтвердить что ты не робот

Привязка модели базовой модели к марионетке ItemView - блокировка .fetch()?

Это вопрос из двух частей. 1) Есть ли лучший способ визуализации модели для представления асинхронно? В настоящее время я делаю запрос ajax с использованием метода fetch в модели (хотя я вызываю его явно при инициализации), а затем создаю шаблонное представление с использованием события приложения 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
4b9b3361

Ответ 1

С очень простой точки зрения, отбросив конкретный пример, который вы предоставили, я хотел бы подойти к проблеме и решению.

Общая задача/решение

Вот общая версия проблемы:

  • Вам нужно получить модель по ее идентификатору.
  • Вам нужно просмотреть рендер после того, как модель была извлечена.

Это довольно просто. Прикрепите модель к представлению перед извлечением данных, а затем используйте событие "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();

Примечания:

Я настраиваю модель с ее идентификатором и представлением с моделью перед вызовом fetch на модели. Это необходимо для предотвращения состояния гонки между загрузкой данных и визуализации представления.

Я указал здесь общий материал Backbone. Marionette, как правило, работает одинаково, но делает рендеринг для вас.

Ваши конкретные потребности

Блокировка выборки

Плохая идея, все вокруг. Не пытайтесь.

Блокирующая выборка сделает ваше приложение полностью безответственным, пока данные не вернутся с сервера. Это проявится как приложение, которое работает плохо и зависает в любое время, когда пользователь пытается что-либо сделать.

Ключом к тому, чтобы не делать этого, является использование событий и обеспечение того, чтобы ваши события были настроены до того, как вы действительно сделаете асинхронный вызов, как показано в моем общем примере.

И не вызывайте выборку из инициализатора модели. Это требует неприятностей, так как вы не сможете настроить какие-либо представления или события до того, как произойдет выбор. Я уверен, что это решит большинство проблем, которые вы испытываете при асинхронном вызове.

События между представлением и моделью

Во-первых, я бы не использовал MyApp.vent для связи между моделью и экземпляром представления. В представлении уже есть ссылка на модель, поэтому они должны общаться напрямую друг с другом.

Другими словами, модель должна непосредственно инициировать событие, и представление должно прослушивать событие на модели. Это работает так же, как мой простой пример, но вы можете заставить свою модель запускать любое событие, которое вы хотите в любое время.

Я также был бы уверен в использовании функции bindTo в представлениях Marionette, чтобы помочь в очистке событий при закрытии представления.

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

live app: http://bbclonemail.heroku.com/


Я не думаю, что я прямо отвечаю на ваши вопросы так, как вам может захотеть, но идеи, которые я представляю, должны привести вас к тому, что вам нужно. Надеюсь, это поможет хотя бы.:)

Ответ 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);
  }
});