【问题标题】:Refresh observableArray when items are not observables当项目不是可观察的时刷新 observableArray
【发布时间】:2012-10-25 06:24:32
【问题描述】:

基本上我有一个 observableArray 并且每个项目的值都不是可观察的。这意味着当我更改项目值时,observableArray 的 foreach 循环中的 UI 不会相应更新。

如果我必须将它们设置为可观察,这意味着巨大的变化,那么有没有一种方法可以手动刷新 UI 或 observableArray foreach?

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    是的,您可以为您的数组调用valueHasMutated 函数:

    yourArray.valueHasMutated();
    

    编辑: 如果首先没有帮助,您可以进行“脏”刷新:

    self.refresh = function(){
        var data = self.array().slice(0);
        self.array([]);
        self.array(data);
    };
    

    这是工作小提琴:http://jsfiddle.net/vyshniakov/FuEy6/2/

    【讨论】:

    • 为什么需要调用 slice(0) 来复制数组?以下效果也更好var data = self.array();self.array(null);self.array(data);
    • 如果数组绑定生成的 HTML 很大,这种脏刷新会很昂贵。如果数组有很多元素,或者每个元素呈现一个大模板或一大块 HTML 元素,就会发生这种情况。如果这对你来说是个问题,你可以使用下面我的解决方案,它更便宜。
    • 在处理数组时,Knockout 确实是一个 POS。为什么它不能更像 Angular - 即工作!?
    【解决方案2】:

    当你有一个包含不可观察元素的可观察数组,并且数组中某个元素的某些属性发生变化时,如果你只想刷新那个元素,你可以使用indexOfsplice,像这样:

    var changedIdx = obsArray.indexOf(changedItem);
    obsArray.splice(changedIdx , 1); // removes the item from the array
    obsArray.splice(changedIdx , 0, changedItem); // adds it back
    

    它的作用是在数组中查找元素,将其删除,然后将其重新插入。当它被插入回来时,元素会再次绑定新值。

    如果你喜欢这个解决方案,你可以扩展所有 ko 可观察数组的功能,像这样:

    ko.observableArray.fn.refresh = function (item) {
        var index = this['indexOf'](item);
        if (index >= 0) {
            this.splice(index, 1);
            this.splice(index, 0, item);
        }
    }
    

    然后,当您更改数组的一项时,您只需进行以下调用:

    obsArray.refresh(changedItem);
    

    如果您的数组中有许多元素,您将获得与 Artem Vyshniakov 的 dirty 刷新相关的改进性能,它会更新数组中所有元素的绑定,而不仅仅是更改的元素。

    注意:valueHasMutated,除了没有记录(我想是供内部使用),只检查数组本身是否发生了变化,而不是数组中不可观察的元素是否发生了变化,所以它不起作用。 IE。它只检测您是否向数组中添加了元素、从数组中删除了元素、使用新的不同元素更改了数组的元素,或者更改了元素的顺序。但它不会检查元素本身是否发生了变化

    【讨论】:

    • 请注意 obsArray.indexOf() 使用 === 查找与它的值匹配的元素,但在 javascript 中 {'x': 'X'} === {'x': 'X'} 返回 false
    • 你是对的。但是,在这种情况下您有一个对要修改的数组元素的引用。您不是在修改一个看起来像数组中的对象,而是在修改数组中的对象。所以,使用indexOf 会找到它。如果更改了不在数组中的对象,则必须将数组中的对象替换为新对象,这与更改数组中的元素的属性并通知它是不同的变了,另一种情况完全不同。
    • 从效率和内存的角度来看似乎效率很低,从数组中间删除一个项目意味着将所有项目引用复制到一个新数组中,然后将一个项目插入到数组中间意味着所有项目的另一个副本对新数组的引用。
    【解决方案3】:

    我最终使用了上面的脏方法,但这样做是为了让我的所有可观察数组都具有此功能。

    ko.observableArray.fn.refresh = function () {
            var data = this().slice(0);
            this([]);
            this(data);
        };
    

    valueHasMutated 对我不起作用。整个列表都必须更新,这有点蹩脚。或者在我的情况下,找到一种方法来扩展 ko 映射插件以用可观察对象填充可观察数组..

    【讨论】:

    • var data = this()/*.slice()*/;this(null);this(data); 也能正常工作,甚至更快
    【解决方案4】:

    我通过替换数组中的整个对象找到了一个简单的解决方案。

    在此示例中,参数ix 是索引,oLine 是更新后的对象,oVM.objProps.lines 包含数组。该解决方案非常有效。

    /* This contortion causes the computed function to fire because
     * we have replaced the entire line object.
     */
    function forceRefresh(ix,oLine) {
      var oVM = pme.getVM();
      oLine = JSON.parse(JSON.stringify(oLine));
      oVM.oObjProp.lines[ix] = oLine;
    }
    

    【讨论】:

      【解决方案5】:

      我正在使用带有 deferUpdates 的 Knockout,而 JotaBe 的解决方案需要更新。

      似乎 Knockout 在删除/添加时检测到这是同一个项目,因此不要刷新数组。

      我采用了以下解决方案:

      ko.observableArray.fn.refresh = function (item, index) {
          if (index==null) index = this['indexOf'](item);
          if (index >= 0) {
              this.splice(index, 1, ko.utils.extend({}, item)) // create new item
              //this.splice(index, 1);
              //this.splice(index, 0, item);
          }
      }
      

      它会正确刷新数组! :-)

      注意,我在 refresh 函数中添加了第二个参数,用于在给定索引时指定索引,以避免 indexOf 运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-11
        • 1970-01-01
        • 1970-01-01
        • 2012-11-15
        相关资源
        最近更新 更多