【问题标题】:Proper way to observe ChildViews in Ember?在 Ember 中观察 ChildView 的正确方法?
【发布时间】:2013-03-01 18:38:08
【问题描述】:

这是fiddle for this question(确保您已打开控制台):

基本上,我有一个把手块视图作为父级,然后循环遍历一组项目并为每个项目创建子块视图。后来,那些孩子被某种方式修改了,我想检测并处理那个事件。

摘要代码:

{{#view App.outerView}}
    {{#each item in items}}
        <h6>Person:</h6>
        {{#view App.innerView}}
            <dl>
                <dt>First Name</dt><dd>{{item.first}}</dd>
                <dt>Last Name</dt><dd>{{item.last}}</dd>
            </dl>
        {{/view}}
    {{/each}}
{{/view}}

稍后:

controller.get( 'items' ).pushObject( 'new item' );

在 jsFiddle 中,我试图保持最后一个子对象始终突出显示(活动 = true)。当最初插入 outerView 时,我们将高亮应用到最后一项,它按预期工作。稍后,当我们添加一个新项目时,我们的 highlight 方法会触发,但它看不到新项目。

关于这个的2个问题:

  • 主要问题:为什么最后一项 (Matt) 没有突出显示?换句话说,为什么在添加新项目后,childViews.length 仍然报告旧值?有没有更好的方法来做到这一点?

  • 次要问题:为什么我们添加新项目时childViews 上的观察者会触发两次? (总共 3 次:初始插入 1 次,添加 1 个新子项时 2 次)


编辑:我稍微调整了小提琴以更好地说明我的需求。注意outerView(HTML + innerViews)的混合内容。基本上,我的 outerView 需要接受任何内容作为子项,而不仅仅是视图/数据的数组/集合。

编辑 2:进一步澄清:事后操作这些子视图是另一回事,可以通过使用 childViews(余烬视图)的内容或使用 jQuery 来操作来完成可能不是正式的 ember 视图对象或任何其他方式的 HTML 元素。 这里的真正目标只是在任何 outerView 内容发生变化时捕获事件(使用 childViews 的当前副本)。

编辑 3:这是Issue on Github

【问题讨论】:

  • 奇怪的是外部视图没有更新它的 childViews 。您可以使用 CollectionView 完成您现在拥有的工作,但这可能无法达到您的目的?这是这种方式的小提琴jsfiddle.net/3Up2M
  • @geekingreen 感谢这个小提琴。它显示了我所期望的工作方式。但是,在这种特殊情况下,我需要能够使用块视图而不是数组绑定。不确定是否有办法让您的示例以这种方式工作。
  • 我认为你做错了...你尝试使用 ContainerView 了吗???
  • 可能发生的情况是 childViews 实际上是中间虚拟视图的子项,而您看到的是 childViews 数组的陈旧副本,因为它们实际上不是您的 childViews。问题是为什么观察者会开火呢?嗯。
  • 如果你在 foobar 观察者中放置一个断点,并检查 this._childViews 中的实际 childView,你可以看到 this._childViews[0]._childViews 包含了新创建的尚未渲染的名称,但是这个._childViews[0].childViews 没有。然而,后者应该影响您的 outerView 的 childViews 属性,而不是前者。好奇。

标签: javascript model-view-controller ember.js javascript-framework


【解决方案1】:

使用Ember.CollectionView

可以更好地完成您在小提琴中完成的工作

然后在active 类的itemViewClass 上添加一个计算属性,该属性确定孩子是否是最后一个孩子

active: function () {
  return this.get("elementId") === this.get("parentView.childViews.lastObject.elementId");
}.property('parentView.childViews.length'),

这是Working Fiddle

注意 每当需要处理视图集合时,请根据您的要求使用 CollectionView 或 Ember.ContainerView(这可以更好地控制子视图操作),但是对于大多数情况下Ember.CollectionView 就足够了

【讨论】:

  • 感谢您抽出宝贵时间回答!我已经编辑了问题以包含来自 cmets 的一些附加信息。您的解决方案是完美的,除了一件事:我需要能够将任何 HTML 作为 outerView 的子项。
  • 所以“任何 html”是指 outerView 必须有不同的子视图,但是最后一个应该有那个“活动”类?
  • 请查看更新的小提琴(抱歉,花了比我预期的时间长一点)。基本上,outerView 的内容需要是传入的任何内容。可以是所有视图,可以是所有 HTML,可以是混合的。
【解决方案2】:

您的初始代码看起来有点混乱。这是实现相同结果的更好方法:

http://jsfiddle.net/XaN8T/1/

您应该将每个项目包装在一个控制器中,这很容易使用 {{each}} 助手完成:

<script data-template-name="application" type="text/x-handlebars">
    This is a list:
    <ul>
        {{each controller itemController="item" itemViewClass="App.ItemView"}}
    </ul>
</script>

并为您的项目创建一个单独的模板:

<script data-template-name="item" type="text/x-handlebars">
    <h6>Person:</h6>
    <div {{bindAttr class=":content active"}}>
        <dl>
            <dt>First Name</dt><dd>{{first}}</dd>
            <dt>Last Name</dt><dd>{{last}}</dd>
        </dl>
    </div>
</script>

然后,您可以通过 active 属性在 Ember 中利用 needs 和计算属性:

App.ItemController = Ember.ObjectController.extend({
    needs: ['application'], //This way each ItemController can access the ApplicationController's content (i.e. the list of all items)
    active: function() {
        return this.get('controllers.application.lastObject') === this.get('content');
    }.property('controllers.application.lastObject')
});

App.ItemView 然后将其 css 类绑定到控制器的 active 属性:

App.ItemView = Ember.View.extend({
    tagName: 'li',
    templateName: 'item',
    classNameBindings: ['controller.active']
});

瞧!它有效:)

【讨论】:

  • 这看起来很有希望:) 我以前从未见过needs。我现在没有时间玩这个,但我期待着它。谢谢!!
  • 显然没有玩过这个来探索它,但是如果我们需要做一些比类绑定更复杂的事情怎么办?也许我们需要在 childViews 更改时重新验证表单,或者......我不知道。这种方法还能用吗?
猜你喜欢
  • 2020-05-13
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多