Knockout forEach在使用排序时不显示新项目

时间:2014-07-29 22:47:37

标签: knockout.js

您好,感谢您一看。

Screen shot 我有一个非常深层次的动态调查:

<assessment>
    <criterionTypes>
      <criterionType>
        <criteria>
          <criterion>
            <responses>
              <response>
                <assessmentResponses>
                  <assessmentResponse>

偶尔,我会让数组中的一些可观察项目以正确的顺序绑定到表单。

enter image description here  尽管我迭代了该集合,但在将其交给视图之前调整排序,甚至在Chrome调试器中以正确的顺序查看该列表。我能够通过在foreach中为那些表现不佳的集合应用排序例程来解决这个问题。像这样:

  <!-- ko foreach: criterionTypes -->
  <div data-bind="foreach: criteria.sort($root.context.sort.criteria),

<!-- ko foreach: assessmentResponses.sort(function (l, r) { return (l.id() == r.id()) ? (l.id() > r.id() ? -1 : 1) : (l.id() > r.id() ? -1 : 1) }) ... -->

到目前为止一切顺利。但是,我有一个用户可以添加新行的功能,当模型在淘汰赛中更新时,UI不会反映这些变化。因此,如果我删除排序,模型绑定将起作用,UI将按预期更新。

在我的更新按钮中,我尝试在点击事件中重新绑定:

var addResponse = function (response) {
        core.addResponse(assessment(), response); 
        ko.applyBindings(assessment);
    };

但无论我尝试绑定什么对象(评估,响应等),我都会遇到同样的错误

未捕获错误:无法解析绑定。 消息:ReferenceError:未定义路由器; 绑定值:compose:{model:router.activeItem,             afterCompose:router.afterCompose,             过渡:&#39;入口&#39;}

我不知道如何继续这样做。也许是一个可以对foreach执行排序的自定义绑定,但是我无法解决这个问题(双关语)。

    <div id="boolean.secondaryResponse" data-bind="if: isSecondaryResponse(), visible: $parent.showSecondaryResponse()>
<!-- ko foreach: assessmentResponses.sort(function (l, r) { return (l.id() == r.id()) ? (l.id() > r.id() ? -1 : 1) : (l.id() > r.id() ? -1 : 1) }) -->                      
                          <!-- ko if: customResponse().template() == 'ingredientSource'-->
                          <div class="col-md-2 col-lg-2" style="z-index: 10"><select data-bind="value: explanation, options: $root.controls.ingredientOrigins, event: { change: $root.context.selectionChanged }, attr: { class: 'form-control ' + criterionCode() + ' ordinal-' + customResponse().ordinal() } " class="remove"></select> &nbsp;</div>&nbsp;
                          <!-- /ko -->
                          <!-- ko if: customResponse().template().startsWith('span')-->
                          <div data-bind="attr: { class: customResponse().template() + ' col-sm-2  col-md-4' }" style="margin-left: -10px; z-index: 10; height: 69px"><input type="text" data-bind="value: textualResponse, attr: { class: 'form-control auto' + customResponse().name() + ' ordinal-' + customResponse().ordinal(), placeholder: customResponse().placeholder }" class="remove" /></div>
                          <!--/ko -->
                          <!-- /ko -->
                          <!-- /ko -->
</div>

2 个答案:

答案 0 :(得分:2)

如何处理排序数组取决于您的应用程序设计。最重要的问题通常是“排序对模型重要还是仅对视图(UI)?”

为视图排序

如果它对视图唯一重要,那么您无需担心视图模型中的数据是否已排序。您只需对视图中显示的内容进行排序即可。为此,您可以绑定到已排序的数组的副本:

foreach: criteria.slice(0).sort(criteriaSortingFunction)

您也可以在视图模型中使用计算的observable来帮助保持视图清洁:

this.sortedCriteria = ko.computed(function () {
    return criteria.slice(0).sort(criteriaSortingFunction);
}, this);

为模型

排序

如果在模型中对数据进行排序很重要,那么一种方法是确保在更新observable之前对数据进行排序:

this.addCriteria(toAdd) {
    var rawArray = this.criteria();
    rawArray.push(toAdd);
    rawArray.sort(criteriaSortingFunction);
    this.criteria(rawArray);
}

如果你在很多地方更新数组,这可能会重复。或者,您可以添加一个扩展器来保持数组的排序:

ko.extenders.sorted = function (obs, sortFunction) {
    obs.sort(sortFunction);
    obs.subscribe(function (array) {
        array.sort(sortFunction);
    });
}

这可以在视图模型构造函数中应用,如下所示:

this.criteria = ko.observableArray(initialCriteria).extend({sorted: criteriaSortingFunction});

答案 1 :(得分:1)

除了迈克尔的回答之外,我想澄清对ko observableArray.sort()的一些理解。

尝试使用控制台

> var arr = ko.observableArray([3,1,4,2]);
> var b = arr.sort();
> arr();  // arr.sort() mutate arr itself
< [1, 2, 3, 4]
> ko.isObservable(b); // result of arr.sort() is not a observable
< false
> b
< [1, 2, 3, 4]

在ko中,绑定中的任何非平凡表达式都会自动换行为ko.computedko.dependentObservable)。对于data-bind="foreach: arr.sort()",ko将构建ko.computed(function() { return arr.sort();})

这里的问题是arr.sort()没有触发ko中的自动依赖跟踪机制。

在控制台中尝试此操作

// k1 is not dependent on arr!
// (I'm not sure whether this is intended in knockout)
> var k1 = ko.computed(function() { return arr.sort();});
> k1.getDependenciesCount();
< 0
> arr.getSubscriptionsCount();
< 0

// k2 is dependent on arr!
> var k2 = ko.computed(function() { return arr().sort();});
> k2.getDependenciesCount();
< 1
> arr.getSubscriptionsCount();
< 1

这是一个演示,显示k1没有响应arr更改,但k2确实如此。 http://jsfiddle.net/gfHz3/8/

因此,一个简单的解决方法是使用assessmentResponses().sort(...)

sort(...)上的原生assessmentResponses()仍会改变assessmentResponses所持有的内容。在Michael的回答中,他使用slice(0)来修复依赖性问题,它还具有在排序之前复制数组的效果。就个人而言,我总是使用非破坏性的underscore sortBy函数。

顺便说一句,还有一件事,这一行

return (l.id() == r.id()) ? (l.id() > r.id() ? -1 : 1) : (l.id() > r.id() ? -1 : 1);

完全相同
return l.id() > r.id() ? -1 : 1;

我想你试着写下面的内容?

return (l.id() == r.id()) ? 0 : (l.id() > r.id() ? -1 : 1);