【问题标题】:Rendering a recursive structure using CompositeView while encapsulating each Model in an ItemView with Marionette使用 CompositeView 渲染递归结构,同时使用 Marionette 将每个模型封装在 ItemView 中
【发布时间】:2013-10-14 19:32:26
【问题描述】:

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

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

问题来了。在此基础上,我遇到了这个问题:Events fired for whole hierarchy 这对于我的事件如何绑定是有意义的。起初,我只是简单地添加了独特的选择器(将 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 on nested structure and CompositeViewDavid Sulc on Nested Views,但我仍然觉得我遗漏了一些重要的细节。

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

祝大家有个愉快的夜晚。 感谢您的宝贵时间。

【问题讨论】:

标签: javascript backbone.js recursion marionette


【解决方案1】:

我将用不同的语言向你解释 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选择器&gt;,例如

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 
        }
    }
});

【讨论】:

  • 这段代码和我发布的第一个代码有什么区别?没有...我有这个工作非常感谢你。我正在寻找一种方法来封装生成的 html,因此最里面的视图只能访问其 html,而不是整个层次结构 html。
  • 好的,抱歉,误读了您的帖子,诚实的错误。为什么不添加直接后代选择器&gt; 以便仅检查直接后代发生的 DOM 事件?如果这对您来说还不够,我宁愿在事件传播方面解决问题:在递归视图中停止事件冒泡。
  • 好的,首先,感谢您抽出宝贵时间回答!所以你提出的解决方案确实有效,它们实际上正是我目前所拥有的。问题真的是我觉得这很hacky,我想把所有东西都干净地封装在一个ItemView中。但是玩弄代码,我得出的结论是,这可能真的不可能,因为这样做显然会失去递归性。然后你发现自己重新实现它..你有什么暗示吗?如果没有其他问题,我会标记你的答案,即使它实际上并没有帮助我,但你确实花时间!
  • 问题是,事件冒泡并没有发生在backbone.view/marionette.view 的范围内,而纯粹是在DOM 本身内。骨干/牵线木偶侧没有发生事件调度,您可以禁用它。这里的事件冒泡纯粹是 DOM 的“特性”。因此,您必须在 DOM 中构建一个平面层次结构,因为事件只会冒泡到它们的父级,而不是它们的兄弟级。然后,您将需要适当的 CSS 或 JS 代码来创建视错觉,即所讨论的 DOM 元素实际上处于视觉层次结构中...... :)
  • event.stopPropagation() 因此是中断 DOM 标准功能的“黑客”。在您的代码中的其他地方,这确实可能被认为是 hacky、sincy,您可能希望该事件会冒泡。因此,具有直接后代选择器的第一个解决方案通常更可取。但我认为有比以下解决方案(2)更严重的罪行。
猜你喜欢
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 2013-10-08
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
相关资源
最近更新 更多