在下面的代码中,我向可观察数组添加了一个项。这导致所有以前的项目在执行.push时执行其计算的可观察项。这是不必要的。如何防止这种情况发生?我拥有的项目越多,这意味着计算出的每个可观察项目都将运行。
参见小提琴示例。http://jsfiddle.net/k8kbdnnv/3/
html代码
<div id="logs"></div>
<table>
<tbody data-bind="foreach: CompanySearchRows">
<tr >
<td>
<input data-bind='textInput: ClientReference' type="text" />
</td>
<td>
<input data-bind='textInput: CompanySearchReference' type="text" />
</td>
</tr>
</tbody>
</table>
jscode
var viewModel;
var maxTitleRefRows = 50;
function ViewModel() {
var self = this;
self.CompanySearchRows = new ko.observableArray([new ItemViewModel()]);
self.Executions = ko.observable(0);
self.addRow = function (clientReference) {
var newRow = new ItemViewModel();
self.CompanySearchRows.push(newRow); //this causes the code to jump
into each array item's OnCompanySearchReferenceChanged computed observable
};
}
function ItemViewModel() {
var self = this;
self.ClientReference = ko.observable("");
self.CompanySearchReference = ko.observable("");
self.OnCompanySearchReferenceChanged = ko.computed(function () {
var reference = self.CompanySearchReference();
logIt("computed executed" + reference);
if (reference !== "" ) {
var currentIndex = viewModel.CompanySearchRows.indexOf(self);
viewModel.Executions(currentIndex);
var length = viewModel.CompanySearchRows().length;
var lastItemIndex = length - 1;
if (currentIndex === lastItemIndex && length < maxTitleRefRows ) {
viewModel.addRow(self.ClientReference());
}
}
return;
});
}
var logs=document.getElementById('logs');
function logIt(msg){
var e=document.createElement('div');
e.innerHTML=msg;
logs.insertBefore(e,logs.firstChild);
}
$(document).ready(function () {
viewModel = new ViewModel();
ko.applyBindings(viewModel);
});
computed
函数包含对主ViewModel中observableArray
的引用。这意味着,如果数组发生变化(例如,从push
),则会重新评估所有项的计算函数。
读取代码时,计算函数的意图似乎是在第二个字段中输入值时添加新行。因此,我们只需要跟踪最后一个项目即可完成此操作。通常建议不要在子对象内部这样做-主视图模型应该管理数组,子对象应该独立于它
为了实现这一点,我在CompanySearchReference
observable上使用了订阅,但仅在最后一项上使用。当该值更改时,父ViewModel将添加新行,并删除前一行上的订阅,因此在任何时候都只有一个订阅处于活动状态。
// subscribe to changes on the child's search reference
// when this has a value, create a new row
var subscription = newRow.CompanySearchReference.subscribe(function (newValue) {
if (newValue && self.CompanySearchRows().length < maxTitleRefRows) {
// value has been changed: first remove this subscription
logIt("removing subscription");
subscription.dispose();
// add a new row (creates a new subscription on new row)
logIt("adding new row");
self.addRow();
}
});
完整的JSFiddle位于http://jsfiddle.net/Quango/fgfw6Ld0/