web-dev-qa-db-ja.com

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メソッドは、gifts配列が空の場合に一度だけ実行され、アイテムをobservableArray()にプッシュまたは削除できますが、問題はありませんが、何も発生しません。

TotalCostを参照してスパンを更新するにはどうすればよいですか?私はそれが何か単純なものだと思います:)

ご協力いただきありがとうございます。

13
Scott

オブザーバブルをアンラップする必要があります。

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
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