【问题标题】:Knockout: a custom binding for observableArray which renders only when complete淘汰赛:observableArray 的自定义绑定,仅在完成时呈现
【发布时间】:2023-03-14 09:20:01
【问题描述】:

好的,标题可能有点混乱,所以我会解释...

我正在编写一个自定义绑定,我将向其传递一个 observableArray。这个 observableArray 是异步填充的,元素一个一个推入。

问题是每次 observableArray 发生突变时都会调用我的自定义绑定(update 方法)。这是有道理的,但在这种情况下没有帮助,因为这意味着第一个元素被渲染 n 次,其中 n 是 observableArray 的长度,第二个元素被渲染 n-1 次,只有 nth 元素被渲染一次。

谁能解释一个简洁的方法,让自定义绑定只在两种情况下都做某事

  • observableArray 已完全填充,或者
  • 何时添加了自定义绑定尚未呈现的元素?

我可以想出几种方法来解决这个问题,在父视图模型上使用一个额外的属性/可观察的作为一个标志,上面写着“完全填充,你现在可以渲染项目”或每个元素上的属性说“你已经渲染了我”。然而,这些都很尴尬,尤其是 observableArray 中的对象也有 observableArray 属性。

对于这个问题没有更好的 Knockout/MVVM 解决方案吗?

更新:为了清楚起见,我正在构建的基础是这样的

<domElmnt data-bind="myBinding: { collection: TypeGroups }" />

在哪里

TypeGroups = ko.observableArray();

其中TypeGroups 中包含的元素都是另一个具有可观察属性的视图模型的实例。

每次我调用 TypeGroups.push(obj) 时,都会再次调用自定义绑定。

【问题讨论】:

  • observableArray 不应重新渲染未更改的元素。尝试添加一个显示 new Date() 的绑定来检查它是否真的重新渲染了东西。类似&lt;span data-bind="text: new Date()"&gt;&lt;/span&gt;
  • 自定义绑定是在observableArray还是items上?
  • observableArray 没有重新渲染;发生的事情是,每次 observableArray 发生突变时,它都会调用自定义绑定,然后循环遍历 observableArray 中的所有项目——即使是那些已经处理过的项目。
  • 您是否有不能使用foreach 绑定的原因?
  • @Michael - 是的:因为一旦进入循环,在任何其他标记之前,就是另一个循环,并且 IE8/9 剥离了这个嵌套循环所需的无容器绑定。在我们发现 IE9 损坏之前,我们确实有一个 foreach,这就是我转向自定义绑定的原因。

标签: javascript knockout.js ko.observablearray custom-binding


【解决方案1】:

无法准确判断是否有必要,但如果必须将 customBinding 放在 observableArray 上,我会这样做。

您想要做的是原子更新。有splice的方法,就像通常的数组拼接一样。

yourArray.splice(atIndex, 0, elem1, elem2, elem3 ...)

不过这样也没什么用:

yourArray.splice.apply(yourArray, [index, 0, elem1, elem2, elem3 ...])

这不是很漂亮,但我们可以做得更好。

function append(array, values) {
    var params = [array().length, 0];
    params.push.apply(params, values);
    array.splice.apply(array, params);
}

我很确定使用 splice 会自动添加所有元素,而无需多次调用 update。另一方面,调用array.push.apply(array, values) 可能就足够了,不需要拼接。

但是!

如果我处于你的位置,我会将绑定从 obserableArray 移动到 observable 数组中的项目。我不知道您为什么在更新后迭代所有项目。如果元素被处理一次,是否有任何理由多次处理已处理的项目?如果没有,那么自定义绑定可能不会在应该使用的地方使用。

如果不进一步了解您的问题,很难说出解决问题的最佳方法是什么。

【讨论】:

  • 恐怕我不明白你的建议。这可能是因为您对我的工作有不同的印象,所以我在我的问题中添加了更多信息。
  • 但是,您的回答确实给了我一些想法:(1)每次调用自定义绑定时,我都可以使用先前呈现的元素的数量拼接数组,以便 splice 将只返回尚未渲染的元素,或者(2)我可以简单地在每次调用时清除先前渲染的元素,因为最后一次调用总是在 observableArray 完成时发生,因此此时枚举其元素将是正确的。抱歉,如果 (1) 与您的回答所建议的差不多。
【解决方案2】:

查看 ko.utils.arrayPushAll()。

您将所有对象转储到一个数组中,并通过一次事务将它们全部推送。这将更新一次 UI。

查看 Ryan Niemeyer 的这篇文章 Utility Functions

【讨论】:

  • 这是个好建议,@Matthew。但是,由于我们将在多个地方使用此自定义绑定,因此我不希望对 observableArray 的填充方式提出要求。我宁愿将此逻辑放在自定义绑定中。此外,您提供的链接没有提及 arrayPushAll()。
【解决方案3】:

这是我解决此问题的方法:每次调用自定义绑定时,我(安全地)计算已经将多少子元素添加到上下文元素:

var childCount = 0,
    children = element.childNodes,
    childMax = children.length;
for (var c = 0; c < childMax; c++) {
    if (children[c].nodeType != 3) {
        childCount++;
    }
}

然后,当我遍历指定的 observableArray 时,我使用 childCount - 它从 previous-element-count 开始 - 作为起始索引,并从该索引开始循环:

var coll = groupsCollection();    // groupsCollection is the specified observableArray
childMax = coll.length;
for (; childCount < childMax; childCount++) {
    // do something with coll[childCount ], such as using it to add a DOM node
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多