问题 ko.Computed()没有使用observableArray进行更新


我有以下代码:

// First we define our gift class, which has 2 properties:
// a Title and a Price.
// We use knockout js validation to ensure that the values input are suitable/ 
function Gift(item)
{
    var self = this;
    self.Title = ko.observable(item.Title);

    // attach some validation to the Title property courtesy of knockout js validation
    self.Title.extend({
        required: true,
        minLength: 3,
        pattern: {
            message: 'At least ',
            params: '^[a-zA-Z]+\s?[a-zA-Z]*'
        }
    });
    self.Price = ko.observable(item.Price);
    self.Price.extend({required:true,number:true,min:0.1,max:1000});
};


var viewModelForTemplated =
{
    gifts: ko.observableArray(),        // gifts will be an array of Gift classes

    addGift: function ()
    {
        this.gifts.push(new Gift({ Title: "", Price: "" }));
    },
    removeGift: function (gift)
    {
        this.gifts.remove(gift);
    },

    totalCost: ko.computed(function () {
        if (typeof gifts == 'undefined')
            return 0;

        var total = 0;

        for (var i = 0; i < gifts().length; i++)
        {
            total += parseFloat(gifts()[i].Price());
        };
        return total;
    })
}


$(document).ready(function ()
{
    // load in the data from our MVC controller 
    $.getJSON("gift/getdata", function (allGifts)
    {
        var mappedgifts = $.map(allGifts, function (gift)
        {
            return new Gift(gift);
        });

        viewModelForTemplated.gifts(mappedgifts);
    });

    ko.applyBindings(viewModelForTemplated, $('#templated')[0]);
}

然后(在脚本上方)

<div id="templated">

<table >
    <tbody data-bind="template: { name: 'giftRowTemplate', foreach: gifts }"></tbody>
</table>

<script type="text/html" id="giftRowTemplate">
    <tr>
        <td>Gift name: <input data-bind="value: Title"/></td>
        <td>Price: \$ <input data-bind="value: Price"/></td>           
        <td><a href="#" data-bind="click: function() { viewModelForTemplated.removeGift($data) }">Delete</a></td>
    </tr>
</script>

<p>Total Cost <span data-bind="text: totalCost"></span> </p>    

<button data-bind="click: addGift">Add Gift</button> 

<button data-bind="click: save">Save</button>

</div>

totalCost方法只运行一次,当礼品数组为空时,我可以将项目推送或删除到observableArray()没问题,但没有任何问题。

如何获得指向totalCost更新的范围?我打赌这很简单:)

谢谢你的帮助。


7514
2017-10-28 16:28


起源

我在调试期间注意到ko.computed(function()中的代码在$(document).ready()代码之前执行。添加到viewmodel中的gifts数组不会导致计算的observable触发该函数。 - Scott
嗨斯科特。关于这个问题的任何动向?我此刻正在努力解决同样的问题。 - Sir Juice


答案:


你需要打开你的observable:

totalCost: ko.computed(function () {
    //also, you forgot typeof below
    if (typeof gifts == 'undefined')
       return 0;

    var total = 0;  //here \/
    for (var i=0; i < gifts().length; i++)
    {                //and here  \/
        total += parseFloat(gifts()[i].Price());
    };
    return total;
})

它没有更新的原因是因为

gifts.length

总是在评估 0,永远不要进入循环。即使它做了,

gifts[i].Price()

不会出于同样的原因;你需要解开观察。


请注意,当您不打开它时,长度评估为零的原因是因为您正在获取 实际可观察​​数组的长度 功能。 Knockout中的所有可观察对象都是作为常规函数实现的;当你不打开它时,你正在击中实际的函数本身,而不是底层的数组。


编辑,

此外,您需要参考礼物 this.gifts,因为它是一个对象属性。这就是为什么这不起作用;礼物是 总是 未定义。

也就是说,你还需要做更多的工作来让ko计算器从对象文字中工作。在这里阅读更多信息:

http://dpruna.blogspot.com/2013/09/how-to-use-kocomputed-in-javascript.html

以下是我制作视图模型的方法:

function Vm{
    this.gifts = ko.observableArray();        // gifts will be an array of Gift classes

    this.addGift = function () {
        this.gifts.push(new Gift({ Title: "", Price: "" }));
    };

    this.removeGift = function (gift)
    {
        this.gifts.remove(gift);
    };

    this.totalCost = ko.computed(function () {
        var total = 0;

        for (var i = 0; i < this.gifts().length; i++)
        {
            total += parseFloat(this.gifts()[i].Price());
        };
        return total;
    }, this);
}

var viewModelForTemplated = new Vm();

12
2017-10-28 16:30



嗨,我尝试了你的建议,但它没有用。我现在已经包含了我的viewmodel和HTML标记,以防出现故障。谢谢你的帮助。 - Scott
@Scott - 控制台中有任何错误吗? - Adam Rackis
嗨亚当,没有Firefox或IE10报告的错误。需要注意的一点是ko.computed()中的代码在document.ready()之前触发,并且在第一次发生时检查ko文档是Knockout评估依赖项然后将它们添加到列表中。可能是因为当typeof(gifts)未定义ko认为没有依赖关系时,计算函数会返回吗? - Scott
@Scott - 啊,就是这样。类型的礼物是 总是 未定义。您可以完全删除该检查;更重要的是,替换所有对礼物的引用 this.gifts - Adam Rackis
@Scott - 同样,对象文字不会像这样工作。根据我希望的最后编辑,可能想要使用构造函数。 - Adam Rackis


你必须打开包装 observableArray 运用 () 在应用索引器时。更新 totalCost 如下:

totalCost: ko.computed(function () {
    if (this.gifts == 'undefined')
       return 0;

    var total = 0;
    for (var i=0; i<this.gifts().length;i++)
    {
        total += parseFloat(this.gifts()[i].Price());
    };
    return total;
})

0
2017-10-28 16:30



嗨,我接受了你的建议,重新展开了观察,但它没有用。我已更新我的OP以显示完整代码。感谢你的协助。 - Scott