ko.computed跟踪数组元素的更改

时间:2012-03-12 07:34:23

标签: knockout.js

有没有办法创建一个ko.computed字段,通知发生了数组元素发生的变化?

我的第一个想法是使用observableArray,但它不起作用,因为

  

An observableArray tracks which objects are in the array, not the state of those objects

不过,我发布这段代码来说明我正在尝试做什么。

HTML:

<div data-bind="foreach:arr">
    <input type="text" value="" data-bind="value: a" />
</div>

<div data-bind="foreach:arr">
    <p>
        Field "a" is changed: <span data-bind="text: aChanged()? 'true': 'false'"></span>
    </p>   
</div>
<p>
    Some "a" field from the array is changed: <span data-bind="text: someAChanged()? 'true': 'false'"></span>
</p>

JavaScript的:

function AppViewModel() {
    this.arr = ko.observableArray([new A(), new A()]);
    this.someAChanged = ko.computed(function () {
        var ch = false;
        var arr = this.arr();
        for (var i = 0; i < arr.length; i ++) {
            if (arr[i].aChanged()) {
                ch = true;
                break;
            }
            return ch;
        }
    }, this);    
}

function A() {
    this.a = ko.observable(1);
    this.aChanged = ko.computed(function() { 
        return this.a() != 1;
    }, this);
}

ko.applyBindings(new AppViewModel());

由于我没有权利回答我自己的问题,我在这里发表我的想法。 我决定使用“订阅”功能。我的解决方案是添加一个“父”链接到数组的元素。每当一个可观察字段发生变化时,依赖于其子节点的父字段也会发生变化:

function Child () {
    this._parent = null;
    this.observableField = ko.observable("");
    this.observableField.subscribe(function (newVal) {
         if (newVal... && this._parent) {
             this._parent.anotherObservableField(...);
         }
    });
}
Child.prototype._setParent(parent) {...} 

3 个答案:

答案 0 :(得分:2)

您需要确保someAChanged computed正在为数组中的每个项目注册依赖项。

这样的事情应该有效:

function AppViewModel() {
    this.arr = ko.observableArray([new A(), new A()]);
    this.someAChanged = ko.computed(function () {
        var ch = false;
        var changedItem = null;
        var arr = this.arr();

        ko.utils.arrayForEach(this.arr(), function(item){
            var changed = item.changed(); //someAChanged registers a change subscription here

            if(changed && !ch){
                ch = true;
                changedItem = item;
            }
        });

        return changedItem;
    }, this);    
};

基本上,这会为您阵列中的每个项目注册“更改”订阅。您之前的代码在找到的第一个代码处停止,这意味着您的数组中没有其他项目添加了“更改”订阅。循环遍历数组中的每个项目并使其成为someAChanged计算的依赖项,如果您的数组中有很多项目,则会很昂贵。

上面的代码也将返回更改的第一个项目。我以为这是你想要的。如果没有,那么重新处理它以返回一组已更改的项目将非常容易。

答案 1 :(得分:0)

我在创建时设置了一个简单的初始值,并检查了我想要结帐的任何字段,如果在计算的observable上更改了这样的话:

    var initPrice = line.Price;
    var initCurrency = line.IdCurrency;

    self.isModified = ko.computed(function() {
        return self.Price() !== initPrice || self.IdCurrency() !== initCurrency;
    });

答案 2 :(得分:0)

我想你几乎就在那里。你的html很好,你的淘汰赛,但你的someAChanged计算功能不正常。如果A已经改变了,你将ch变量设置为true并且跳出for循环但是你实际上从未返回true。实际上你根本不需要ch变量。

function AppViewModel() {
    this.arr = ko.observableArray([new A(), new A()]);
    this.someAChanged = ko.computed(function () {
        var arr = this.arr();
        for (var i = 0; i < arr.length; i ++) {
            if (arr[i]().aChanged()) {
                return true;
            }
        }
        return false;
    }, this);  
}