从viewmodel上的属性中淘汰更新observable集合

时间:2014-02-13 20:44:31

标签: javascript mvvm knockout.js collections observable

我正在使用knockout为项目创建基本的AJAX购物车,并且当视图模型中的属性更新时,视图模型中的可观察集合中包含的各个产品的成本需要更新。我已经尝试了很多个小时的各种解决方案,我希望有人可以指出我正确的方向。我已经包括了一个jsfiddle。

http://jsfiddle.net/g8BLj/3/

var product = function (title, operationName, description, parent) {
  this.title = title;
  this.operationName = operationName;
  this.description = description;
  this.cost = 9;
  this.count = ko.observable(parent.recordCount);
  this.subtotal = ko.computed(function () {
    return this.count * this.cost;
  }).bind(this);
};

order = function () {
 var listName = ko.observable("not defined"),
     listId = ko.observable("not defined"),
     recordCount = ko.observable("not defined"),
     products = [
     new product('Product1', 'EMAIL_VER_DELIVERABLE', 'Description.', this),
     new product('Product2', 'EMAIL_BASIC_NO_SUPRESSION_W_VERIFICATION', 'Description.', this),
     new product('Product3', 'PHONE_PREM', 'Description.', this)],
     total = function () {
        var total = 0;
        $.each(this.products(), function () {
            total += this.subtotal();
        });
        return total;
     };

 // anything in the return block is considered public, anything above is private
 return {
    listName: listName,
    listId: listId,
    recordCount: recordCount,
    products: products,
    total: total
 };
}();

ko.applyBindings(order);

// when this value changes the product Cost needs to be updated
order.listName('test list')
order.listId(1)
order.recordCount(100)

谢谢, 克里斯

2 个答案:

答案 0 :(得分:0)

我认为,解决此问题最重要的是使用订阅order.recordCount更改,而不是将order作为参数发送到product
你可以这样写:

recordCount.subscribe(function (newValue) {
    ko.utils.arrayForEach(products, function (product) {
        product.count(newValue);
    })
});

此外,您应该更改product.subtotal计算:

this.subtotal = ko.computed(function () {
    return this.count() * this.cost;
}, this);

答案 1 :(得分:0)

“this”范围存在一些问题,导致问题。

this.subtotal = ko.computed(function () {
  return this.count * this.cost;
}).bind(this);

使环境范围正确的正确方法是将“this”作为第二个参数传递给ko.computed。此外,count是一个可观察的,你需要评估它。

this.subtotal = ko.computed(function () {
  return this.count() * this.cost;
}, this);

products变量将“this”传递给产品的构造函数。此时的环境是窗口,您使用的是匿名函数/显示模块模式。

所以当

 this.count = ko.observable(parent.recordCount);

计算,父是窗口,所以recordCount == undefined。

如果你想继续使用揭示模块模式,你需要调整order函数来声明你的返回对象(“obj”),然后创建产品。

您还应该将总属性声明为ko.computed。我使用map / reduce而不是$ .each,但这是个人偏好。

完成此操作后,它会显示产品类上count属性的其他问题。 parent.recordCount是一个可观察的,因此您创建一个observable,其值是可观察的,而不是observable的值。只需将observable指定给count属性即可。


var product = function (title, operationName, description, parent) {
    this.title = title;
    this.operationName = operationName;
    this.description = description;
    this.cost = 9;
    this.count = parent.recordCount;
    this.subtotal = ko.computed(function () {
        return this.count() * this.cost;
    }, this);
};

order = function () {
    var
    listName = ko.observable("not defined"),
        listId = ko.observable("not defined"),
        recordCount = ko.observable("not defined"),
        products = [];

    var obj = {
        listName: listName,
        listId: listId,
        recordCount: recordCount,
        products: products
    }

    // now we have an object to push into the product class
    products.push(
        new product('Product1', 'EMAIL_VER_DELIVERABLE', 'Description.', obj),
        new product('Product2', 'EMAIL_BASIC_NO_SUPRESSION_W_VERIFICATION', 'Description.', obj),
        new product('Product3', 'PHONE_PREM', 'Description.', obj)
        );

    obj.total = ko.computed( function() {
        return this.products
            .map(function(item) { return item.subtotal(); })
            .reduce( function(runningTotal, subtotal) { return runningTotal + subtotal;
        }, 0);
    }, obj);

    // anything in the return block is considered public, anything above is private
    return obj;
}();

ko.applyBindings(order);

// when this value changes the product Cost needs to be updated
order.listId(1);
order.listName('test list');
order.recordCount(100);