Knockout.js 日本語ドキュメント

買い物カゴ

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());

side menu