简体   繁体   English

从ViewModel的属性中剔除更新可观察的集合

[英]knockout update observable collection from property on viewmodel

I'm using knockout to create a basic AJAX shopping cart for a project and the cost of the individual products that are contained in an observable collection inside the view model need to update when a property on the view model is updated. 我正在使用淘汰赛为项目创建基本的AJAX购物车,并且在更新视图模型的属性时,视图模型内部可观察集合中包含的各个产品的成本也需要更新。 I've been trying various solutions for many hours and I hope somebody can point me in the right direction. 我已经尝试了各种解决方案很多小时了,希望有人能指出正确的方向。 I've included a jsfiddle. 我包括一个jsfiddle。

http://jsfiddle.net/g8BLj/3/ 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)

Thanks, Chris 谢谢克里斯

I think, the most important thing to solve this problem, is to use subscription to order.recordCount changes instead of sending order as parameter to product . 我认为,解决此问题的最重要方法是使用对order.recordCount更改的订阅,而不是将order作为参数发送给product
You can write something like this: 您可以这样写:

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

Also, you should change product.subtotal calculation: 另外,您应该更改product.subtotal计算:

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

There are a couple of issues with the "this" scope which are causing problems. “ this”范围存在几个问题,这些问题会引起问题。

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

The correct way to get the ambient this scope correct is to pass "this" into the ko.computed as a second argument. 使范围正确的环境的正确方法是将“ this”作为第二个参数传递到ko.compute中。 Also, count is an observable to you need to evaluate it. 此外,计数是可以观察到的,您需要对其进行评估。

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

The products variable is passing "this" into the constructor of product. product变量正在将“ this”传递给product的构造函数。 The ambient this at this point is window, as are you are using anonymous function/revealing module pattern. 此时的环境,这是窗口,是你使用的匿名函数/透露出模块模式。

So when 所以什么时候

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

evaluates, parent is window, so recordCount == undefined. 计算,父级是窗口,因此recordCount == undefined。

If you want to continue to use the revealing module pattern you need to tweek the order function to declare your return object ("obj") and then create the products. 如果要继续使用显示模块模式,则需要对订单函数进行周声明,以声明返回对象(“ obj”),然后创建产品。

You should also declare the total property as a ko.computed. 您还应该将total属性声明为ko.computed。 I have used map/reduce instead of $.each but that is personal preference. 我使用了map / reduce而不是$ .each,但这是个人喜好。

When this is done, it reveals a further issues with the count property on the product class. 完成此操作后,它会揭示产品类上count属性的其他问题。 The parent.recordCount is an observable, so you are creating an observable where its value is an observable, not the value of the observable. parent.recordCount是可观察的,因此您要创建一个可观察的对象,其值是可观察的,而不是可观察的值。 Just assign the observable to a count property. 只需将可观察对象分配给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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM