假设我有observableArray
:
var outerViewModel = {
observableArray: ko.observableArray()
};
然后我们推送一个viewmodel:
var viewModel = {
prop: ko.observable()
// Plus lots of other properties
};
outerViewModel.observableArray.push(viewModel);
我如何知道viewModel
的属性何时发生变化?
更新
这是一个更具体的用例:
我有一个带有属性IsValid
的ViewModel。 IsValid
本身是基于该ViewModel中所有属性的计算字段。
我有一个ViewModel,它包含那些ViewModel的observableArray。我想在该ViewModel上创建一个名为IsValid
的计算字段,当IsValid
在数组中的所有ViewModel上为真时,该字段将为true。
我使用return _.all(models,function(model){return model.IsValid();})
的天真尝试不起作用。
当我向该集合添加一个新项目时,它正确地使外部ViewModel无效,因为该集合会侦听push
,pop
等。但是,更改该集合中的项目,不会触发任何内容,因此即使ViewModel现在可能本身有效,外部集合也无法识别。
答案 0 :(得分:0)
最简单的方法是手动订阅数组中所有项的所有属性,并在可观察数组发生更改时更新此订阅。这是一种蛮力,但它有效。考虑到您可以为所有期望的可观察量订阅一个简单的函数。
如果您想要一种更有效的方法,可以使用ko.projections将数组中的每个项映射到计算的observable,该observable在任何内部属性发生更改时发生更改,并订阅映射中的所有计算的observable阵列。此插件已完全优化,仅重新映射必要的属性。当您对每个项目进行简单订阅时,这会更有效。
另一个蛮力的想法是使用计算的observable来计算数组的JSON表示,就像R Niemeyer在这里解释的isDirty:Creating a Smart, Dirty Flag in KnockoutJS。或者使用kolite的脏标志,在检测到更改后重置它。
您还可以扩展项目并执行manula通知,如下所述:Knockout extenders notify。在这种情况下,您还需要将此通知订阅到数组中的所有项目。
我确信,通过更多信息,这个解决方案可以根据您的具体情况进行微调。如果您需要知道哪些属性或项目发生了变化,或者仅仅是某些内容发生了变化,您想要对检测到的更改做些什么等等,您就没有说明需要它的原因。如果你改进了问题,我可以尝试微调答案。
一旦我知道具体情况,我就可以向您展示一份工作样本。
重要的是要注意,您必须访问数组中的所有元素,以便计算的get订阅所有元素。如果您使代码短路并且无法访问所有代码(例如,在第一个无效的情况下打破循环),则不会创建订阅,这将无法正常工作。我还明确了对可观察数组的依赖性。这实现了allValid
在可观察数组或其任何项的有效属性的任何更改时重新计算。
var vm = {
vals: ko.observableArray([
{ n: 0, valid: ko.observable(true) },
{ n: 1, valid: ko.observable(false) },
{ n: 2, valid: ko.observable(true) }
])
};
vm.allValid = ko.computed(function() {
var valid = true;
// make dependen on observable array
var vals = vm.vals();
// make dependent on all the items
for (var i = 0; i<vals.length; i++) {
valid = valid && vals[i].valid();
}
return valid;
});
vm.make1Valid = function() {
vm.vals()[1].valid(true);
}
vm.make1NotValid = function() {
vm.vals()[1].valid(false);
}
vm.pushValid = function() {
vm.vals.push({n:100, valid:ko.observable(true)})
}
vm.pushNotValid = function() {
vm.vals.push({n:100, valid:ko.observable(false)})
}
vm.pop = function() {
vm.vals.pop()
}
ko.applyBindings(vm);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="foreach: vals">
<div>
n:<span data-bind="text: n"></span>
valid:<span data-bind="text: valid"></span>
</div>
</div>
<hr>
<div style="font-weight:bold">
<b>All valid:<span data-bind="text: allValid"></span> </b>
</div>
<div>
<input type="button" value="Make 1 valid" data-bind="click: make1Valid"/>
<input type="button" value="Make 1 not valid" data-bind="click: make1NotValid"/>
<input type="button" value="Push valid" data-bind="click: pushValid"/>
<input type="button" value="Push not valid" data-bind="click: pushNotValid"/>
<input type="button" value="Pop" data-bind="click: pop"/>
</div>
&#13;
您还可以在此jsfiddle中查看扩展版本。
但我建议你使用已经实现了这种功能的ko.validation插件。验证每个单独的属性以及viewmodel树的所有属性。对于这种情况,请参阅:How to validate an array?