買い物カゴ
Computed Observable がどのように連鎖するかを示したサンプルです。
買い物カゴの各行にはそれぞれの小計を示す ko.computed
プロパティがあり、
これらはさらに合計を示す ko.computed
プロパティに繋がります。
内容を変更すると、その変更が波紋となって Computed Observable の連鎖を伝い、
すべての関連する UI が更新されます。
またこのサンプルでは、ドロップダウンリストをカスケードさせるシンプルな方法も示しています。
デモ
カテゴリ | 製品 | 価格 | 数量 | 小計 | |
---|---|---|---|---|---|
削除 |
合計金額:
コード: View
<table width='100%'> <thead> <tr> <th width='25%'>カテゴリ</th> <th width='25%'>製品</th> <th class='price' width='15%'>価格</th> <th class='quantity' width='10%'>数量</th> <th class='price' width='15%'>小計</th> <th width='10%'> </th> </tr> </thead> <tbody data-bind='foreach: lines'> <tr> <td> <select data-bind='options: sampleProductCategories, optionsText: "name", optionsCaption: "選択...", value: category'> </select> </td> <td data-bind="with: category"> <select data-bind='options: products, optionsText: "name", optionsCaption: "選択...", value: $parent.product'> </select> </td> <td class='price' data-bind='with: product'> <span data-bind='text: formatCurrency(price)'> </span> </td> <td class='quantity'> <input data-bind='visible: product, value: quantity, valueUpdate: "afterkeydown"' /> </td> <td class='price'> <span data-bind='visible: product, text: formatCurrency(subtotal())' > </span> </td> <td> <a href='#' data-bind='click: $parent.removeLine'>削除</a> </td> </tr> </tbody> </table> <p class='grandTotal'> 合計金額: <span data-bind='text: formatCurrency(grandTotal())'> </span> </p> <button data-bind='click: addLine'>製品を追加</button> <button data-bind='click: save'>注文</button>
コード: ViewModel
function formatCurrency(value) { return "$" + value.toFixed(2); } var CartLine = function() { var self = this; self.category = ko.observable(); self.product = ko.observable(); self.quantity = ko.observable(1); self.subtotal = ko.computed(function() { return self.product() ? self.product().price * parseInt("0" + self.quantity(), 10) : 0; }); // カテゴリが変更された時に、製品の選択状態をリセットする self.category.subscribe(function() { self.product(undefined); }); }; var Cart = function() { // 買い物カゴ各行の情報を保持し、それらから合計金額を算出する var self = this; self.lines = ko.observableArray([new CartLine()]); // デフォルトで1行格納する self.grandTotal = ko.computed(function() { var total = 0; $.each(self.lines(), function() { total += this.subtotal() }) return total; }); // アクション self.addLine = function() { self.lines.push(new CartLine()) }; self.removeLine = function(line) { self.lines.remove(line) }; self.save = function() { var dataToSave = $.map(self.lines(), function(line) { return line.product() ? { productName: line.product().name, quantity: line.quantity() } : undefined }); alert("次のようにサーバに送信できます: " + JSON.stringify(dataToSave)); }; }; ko.applyBindings(new Cart());
jsFiddle で試す /
原文はこちら