我有一个包含多个产品的对象,这些产品具有用户可以选择的变体。
我想触发发送所选项目(或选项值)的事件,以便能够计算总和。
我已经尝试过前进,这是我来得最远的地方:
<div class="row" data-bind="foreach: {data: additionalProducts, as: 'product'}">
<div class="col-xs-12 col-sm-6">
<h4 data-bind="text: product.Name"></h4>
<p data-bind="text: product.Description"></p>
<div class="form-group">
<select class="form-control input-block-level"
data-bind="options: product.Variants,
optionsValue: 'Id',
optionsText: function(item) {
return item.Name + ' - ' + item.Price + ' kr'
},
value: $parent.selectedProduct,
optionsCaption: '-- Välj önskat belopp nedan --'"></select>
</div>
</div>
</div>
self.selectedProduct = ko.observable();
self.selectedProduct.subscribe(function (newValue) {
console.log(newValue);
});
现在发生的是控制台首先输出所选选项的Id,然后输出undefined并重置两个选择元素。
其他产品数据如下所示:
[{"Id":1,"Name":"Finisher t-shirt","Description":"Köp minne från loppet. Snygg specialdesignad t-shirt i funktionsmaterial. Hämtas ut i samband med nummerlappsutdelningen.","Variants":[{"Id":0,"Name":"Ingen t-shirt","Price":0.0},{"Id":1,"Name":"Storlek XS","Price":100.0},{"Id":2,"Name":"Storlek S","Price":100.0},{"Id":3,"Name":"Storlek M","Price":100.0},{"Id":4,"Name":"Storlek L","Price":100.0},{"Id":5,"Name":"Storlek XL","Price":150.0}]},{"Id":2,"Name":"Donation till Amzungo","Description":"Donera gärna en slant till välgörenhet.","Variants":[{"Id":9,"Name":"Jag vill inte donera","Price":0.0},{"Id":10,"Name":"Jag vill donera","Price":25.0},{"Id":11,"Name":"Jag vill donera","Price":50.0},{"Id":15,"Name":"Jag vill donera","Price":75.0},{"Id":12,"Name":"Jag vill donera","Price":100.0},{"Id":13,"Name":"Jag vill donera","Price":150.0},{"Id":14,"Name":"Jag vill donera","Price":200.0}]}]
提前致谢。
1 个答案:
答案 0 :(得分:1)
I would recommend splitting the task into several independent bits. You have:
A product variant. It keeps track of its Id, Name, Price and calculated value for the DisplayText.
function Variant(data) {
ko.utils.extend(this, data);
this.DisplayText = this.Name + ' - ' + this.Price + ' kr';
}
A product. It keeps track of its Id, Name, Description and Variants, as well as the SelectedVariant.
function Product(data) {
ko.utils.extend(this, data);
this.Variants = ko.utils.arrayMap(data.Variants, function (v) {
return new Variant(v);
});
this.SelectedVariant = ko.observable();
}
A product selector. It keeps track of all the Products and calculates the Total:
function ProductSelector(data) {
var self = this;
self.Products = ko.utils.arrayMap(data, function (p) {
return new Product(p);
});
self.Total = ko.computed(function () {
var total = 0;
ko.utils.arrayForEach(self.Products, function (p) {
var selectedVariant = p.SelectedVariant();
total += selectedVariant ? selectedVariant.Price : 0;
});
return total;
});
}
Now you can bind a view to this:
<div class="row" data-bind="foreach: Products">
<div class="col-xs-12 col-sm-6">
<h4 data-bind="text: Name"></h4>
<p data-bind="text: Description"></p>
<div class="form-group">
<select class="form-control input-block-level" data-bind="
optionsCaption: '-- Välj önskat belopp nedan --',
options: Variants,
optionsText: 'DisplayText',
value: SelectedVariant
"></select>
</div>
</div>
</div>
<p><b>Total:</b> <span data-bind="text: Total"></span> kr</p>
Note that you don't need the optionsValue binding if you want knockout to store the selected object itself (as opposed to one of its property values).
I've used ko.utils.extend(this, data); to initialize objects with the data you pass in. This is convenient and good enough for simple use cases like this one. For more complex cases, have a look at the official mapping plugin or the Knockout Viewmodel Plugin.
With these bits in place you can handle your input easily. Expand and run the snippet below to see it work.
function Variant(data) {
ko.utils.extend(this, data);
this.DisplayText = this.Name + ' - ' + this.Price + ' kr';
}
function Product(data) {
ko.utils.extend(this, data);
this.Variants = ko.utils.arrayMap(data.Variants, function (v) {
return new Variant(v);
});
this.SelectedVariant = ko.observable();
}
function ProductSelector(data) {
var self = this;
self.Products = ko.utils.arrayMap(data, function (p) {
return new Product(p);
});
self.Total = ko.computed(function () {
var total = 0;
ko.utils.arrayForEach(self.Products, function (p) {
var selectedVariant = p.SelectedVariant();
total += selectedVariant ? selectedVariant.Price : 0;
});
return total;
});
}
var vm = new ProductSelector([
{
"Id": 1,
"Name": "Finisher t-shirt",
"Description": "Köp minne från loppet. Snygg specialdesignad t-shirt i funktionsmaterial. Hämtas ut i samband med nummerlappsutdelningen.",
"Variants": [
{
"Id": 0,
"Name": "Ingen t-shirt",
"Price": 0
},
{
"Id": 1,
"Name": "Storlek XS",
"Price": 100
},
{
"Id": 2,
"Name": "Storlek S",
"Price": 100
},
{
"Id": 3,
"Name": "Storlek M",
"Price": 100
},
{
"Id": 4,
"Name": "Storlek L",
"Price": 100
},
{
"Id": 5,
"Name": "Storlek XL",
"Price": 150
}
]
},
{
"Id": 2,
"Name": "Donation till Amzungo",
"Description": "Donera gärna en slant till välgörenhet.",
"Variants": [
{
"Id": 9,
"Name": "Jag vill inte donera",
"Price": 0
},
{
"Id": 10,
"Name": "Jag vill donera",
"Price": 25
},
{
"Id": 11,
"Name": "Jag vill donera",
"Price": 50
},
{
"Id": 15,
"Name": "Jag vill donera",
"Price": 75
},
{
"Id": 12,
"Name": "Jag vill donera",
"Price": 100
},
{
"Id": 13,
"Name": "Jag vill donera",
"Price": 150
},
{
"Id": 14,
"Name": "Jag vill donera",
"Price": 200
}
]
}
]);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="row" data-bind="foreach: Products">
<div class="col-xs-12 col-sm-6">
<h4 data-bind="text: Name"></h4>
<p data-bind="text: Description"></p>
<div class="form-group">
<select class="form-control input-block-level" data-bind="
optionsCaption: '-- Välj önskat belopp nedan --',
options: Variants,
optionsText: 'DisplayText',
value: SelectedVariant
"></select>
</div>
</div>
</div>
<p><b>Total:</b> <span data-bind="text: Total"></span> kr</p>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>