检测ObservableArray中视图模型的更改

时间:2016-01-18 20:39:44

标签: knockout.js

假设我有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无效,因为该集合会侦听pushpop等。但是,更改该集合中的项目,不会触发任何内容,因此即使ViewModel现在可能本身有效,外部集合也无法识别。

1 个答案:

答案 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>&nbsp;
    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;
&#13;
&#13;

您还可以在此jsfiddle中查看扩展版本。

但我建议你使用已经实现了这种功能的ko.validation插件。验证每个单独的属性以及viewmodel树的所有属性。对于这种情况,请参阅:How to validate an array?