4

我将从一个明确的问题开始,然后解释:
如何正确地将包含在 a 中的模型封装CompositeViewItemView

我的问题很简单,但我无法获得好的工作。我有一棵笔记树。每个音符都是一个 Backbone.model 并且有一个@descendants集合,然后我有一个 Backbone.Collection 来表示那棵树(其中@descendants 是一个实例)我设置了一个基本CollectionView的,作为一个itemView,作为CompositeView。一切都完美呈现,我对此感到非常高兴。该工作解决方案显示在第一段代码中!

问题来了。在此基础上,我遇到了这个问题:针对整个层次结构触发的 事件,这对于我的事件如何绑定是有意义的。起初,我只是添加了独特的选择器(将 data-guid 添加到我的 html 元素中,以便我可以通过它获取它们)并且它工作正常,尽管它已经变得“hacky”。但是其他类似的问题已经出现,所以我决定我需要一个解决方案。所以,我告诉 myslef,让我们将每个模型封装在一个 ItemView 中,并使用 Composite 视图递归地渲染它们。但是……这并不是世界上最好的……

这是我一开始的:

class Note.ModelView extends Marionette.CompositeView
  template: "note/noteModel"
  id: "note-item"
  itemViewContainer: ".note-descendants"
  ui:
    noteContent: ".noteContent"
  events:
    "keypress .noteContent": "createNote"
    "click .destroy": "deleteNote"
  # ...
  initialize: ->
    @collection = @model.descendants
  # ...

class Note.CollectionView extends Marionette.CollectionView
  id: "note-list"
  itemView: Note.ModelView
  initialize: ->
    @listenTo @collection, "sort", @render

现在我将所有属于渲染模型的东西都转移到了一个新的 ItemView

  class Note.ModelView extends Marionette.ItemView
    template: "note/noteModel"

    ui:
      noteContent: ".noteContent"
    events: ->
      guid = @model.get 'guid'
      events = {}
      events["keypress #noteContent#{guid}"] = "createNote"
      events["blur #noteContent#{guid}"] = "updateNote"
      events["click #destroy#{guid}"] = @triggerEvent 'deleteNote'

    initialize: ->
      @bindKeyboardShortcuts()
      @listenTo @model, "change:created_at", @setCursor

  class Note.TreeView extends Marionette.CompositeView
    template: "note/parentNote"
    itemView: Note.ModelView

    initialize: ->
      @collection = @model.descendants
      @listenTo @collection, "sort", @render
      _.bindAll @, "renderItem"
    renderItem: (model) ->
      if model.get('parent_id') is 'root'
        itemView = new Note.ModelView model: model
        itemView.render()
        @$el.append itemView.el
    onRender: ->
      @collection.each this.renderItem
      @renderItem @model
    appendHtml: (collectionView, itemView) ->
      @model.descendants.each (note) =>
        iv = new Note.ModelView model: note
        iv.render()
        @.$('.note-descendants').append iv.el


  class Note.CollectionView extends Marionette.CollectionView
    id: "note-list"
    itemView: Note.TreeView
    initialize: ->
      @listenTo @collection, "sort", @render

和 noteMode 模板(parentNote 现在只是一个空模板)

<button class="btn btn-danger btn-xs untab">UN</button>
<button class="btn btn-success btn-xs tab">TAB</button>
<div class="noteContent">{{{indent}}}{{{title}}}</div>
<button class="destroy"></button>
<div class="note-descendants"></div> # That is where I'm putting the children notes

所以这几乎可以工作。但它很老套,而且......好吧,它仍然不能正常工作。我一直在阅读所有文档,并且我已经阅读了关于嵌套结构的 Derick Bailey 和关于嵌套视图的 CompositeViewDavid Sulc这两个链接,但我仍然觉得我错过了一些重要的细节。

作为答案,我正在寻找的是使用 Marionette 管理此问题的任何提示或任何常用方法。我真的在寻找一些干净的东西,因为这将是构建应用程序的角度之一。
也许我也在错误的地方搜索?任何事情都会受到欢迎!!非常感谢

祝大家度过一个愉快的夜晚。感谢您的时间。

4

1 回答 1

3

我将用不同的语言向您解释 Derick Bailey 在您引用的博客文章中已经说过的内容。

意见

您需要两个视图才能完成这项工作。

第一个,“母视图”,是包含递归结构作为其子节点的最外层视图。这可能是 Marionette.CollectionView 或 Marionette.CompositeView。

第二个视图是您的递归视图,这意味着它需要是 Marionette.CompositeView。通过不指定“itemView”属性,您可以递归 Marionette.CompositeView。在缺少 itemView 属性的情况下,Marionette.CompositeView 将使用 ITSELF 来呈现给定集合的模型。

收藏

您需要一个集合,它是嵌套 Backbone.Collections 的 Backbone.Collection,或者只是包含深度嵌套散列的 Backbone.Collection。在后一种情况下,您不必忘记将简单的哈希值转换为 Backbone.Collections,然后将它们交给 Marionette.CompositeView(如下面在 RecursiveView 的 initialize() 方法中所做的那样)。

把它们放在一起

var RecursiveView = Marionette.CompositeView.extend({
    template: '#someTemplate',
    initialize: function () {
        this.collection = this.model.get('children')
    }
});

var MotherView = Marionette.CollectionView.extend({
    itemView: RecursiveView,
    collection: new Backbone.Collection([{key: 'value', children: [{}, {}, {}]}])
});

问题:事件冒泡

自然地,DOM 事件现在会冒泡,所以这段代码

var RecursiveView = Marionette.CompositeView.extend({
    events: {
        'click button': someCallback
    }
});

将是它引用的视图的事件以及它的所有后代的全部内容。

这个问题最简单的解决方案是使用直接后代 css 选择器>,例如

var RecursiveView = Marionette.CompositeView.extend({
    events: {
        'click >button': someCallback
    }
});

如果这还不够,一种有效的解决方案是防止自动事件冒泡,例如

var RecursiveView = Marionette.CompositeView.extend({
    events: {
        'click button': function (event) {
            event.stopPropagation();
            // or event.stopImmediatePropagation() depending on your use case
            // cf. http://stackoverflow.com/a/5299841/899586 and 
        }
    }
});
于 2013-10-08T09:49:04.133 回答