Nesse post vou demostrar como criar um carrinho simples com knockoutjs, também iremos utilizar knockoutjs mapping, veja o exemplo funcionando http://jsfiddle.net/andrebtoe/r9kcfr04/1.

carrinhoExemploKO

A ideia é que tem um carrinho que se seja possível adicionar todos os produtos disponíveis, e não mais que isso, ou seja, não é possível adicionar um mesmo produto mais de uma vez; Também deve ser possível que o usuário defina a quantidade de cada produto, e o usuário deve ver o total e sub total do carrinho.

Primeiro devemos criar uma classe de ViewModel como segue:

function ViewModel(){
	var self = this;
}

$(function(){
	window.viewModel = new ViewModel();
	ko.applyBindings(window.viewModel);
});

Acima temos a classe “ViewModel” e uma instancia dela inserida no KO. Repare que definimos uma variavel “self”, e nela atribuimos o valor de “this”, isso facilita no de contexto dentro de um método, já que o contexto muda dentro de um método do javascript, em outras palavras, o “this” dentro de uma function não é o mesmo de fora. Quando trabalhamos com KO, por convenção é usado a nomenclatura “self”, mas fica a seu critério.
Em seguida vamos adicionar um array de produtos, no nosso caso não é necessário colocar os produtos como observáveis, já que ele não mudará.

function ViewModel(){
	var self = this;
	
	self.Produtos = [
		{Id: 1, Nome: "Lápis", Valor: 0.90},
		{Id: 2, Nome: "Caderno", Valor: 5.60},
		{Id: 3, Nome: "Borracha", Valor: 1.50}
	];
}

Termos em cada item do produto: Id, Nome e Valor.
Vamos adicionar um array observable para gerenciar os produtos adicionados e os métodos para adicionar e remover produtos.

function ViewModel(){
	var self = this;
	
	self.Produtos = [
		{Id: 1, Nome: "Lápis", Valor: 0.90},
		{Id: 2, Nome: "Caderno", Valor: 5.60},
		{Id: 3, Nome: "Borracha", Valor: 1.50}
	];
	
	self.ProdutosAdicionados = ko.observableArray();
	
	self.AdicionarProduto = function(){
		
	};
	
	self.RemoverProduto = function(produto){
		self.ProdutosAdicionados.remove(produto);
	};
}

Adicionamos o array observable “ProdutosAdicionados”, também adicionamos os métodos “AdicionarProduto” (voltaremos para implementar esse métode posteriormente) e “RemoverProduto” que tem como função remover o produto que foi passado por parametro do array observable “ProdutosAdicionados”.
Vamos deixar o javascript de lado um pouco para nos concentrarmos no html. Vamos definir uma tabela que tenha 4 colunas: produto, sub total, quantidade e um botão para remover o produto.

<table class="table table-striped table-condensed table-produtos-cobranca">
	<thead> 
		<tr>
			<th>Produto</th>
			<th>Sub total</th>
			<th>Quantidade</th>
			<th></th>
		</tr>
	</thead>
	<tbody data-bind="foreach: ProdutosAdicionados">
		<tr>
			<td>
				<select></select>
			</td>
			<td>
				<input type="text" class="form-control input-small" />
			</td>
			<td>
			</td>
			<td>
				<button class="btn btn-danger" type="button" data-bind="click: $root.RemoverProduto">Remover</button>
			</td>
		</tr>
	</tbody>
	<tfoot>
		<tr>
			<td colspan="4" class="padding-zero">
				<button class="btn btn-primary margin-top-10" type="button" data-bind="click: AdicionarProduto">Adicionar</button>
			</td>
		</tr>
	</tfoot>
</table>

Acimas temos uma tabela que na tag thead tem os títulos, no tbody faz uso do “foreach” do array observable “ProdutosAdicionados”, para listar os produtos. Olhando mais atentamente podemos perceber que temos uma tag select vazia, onde os produtos deverão ser listados, porem não iremos adicionar até então, por que temos que implementar a lógicas dos produtos já adicionados, também temos um do tipo text que irá controlar a quantidade de produtos, um td vazio que irá mostrar o sub total do item e por último um botão que faz uso do método “RemoverProduto”, repare que ele está com a palavra chave “$root”, isso por que ele está dentro de um loop, e o método não está contido em nenhuma instancia de produto, e sim na “ViewModel”, seguimos em diante.

Vamos voltar para o javascript e criar nossa classe para cada item do carrinho.

function ProdutoItemCarrinho(dados, produtosSelecionados, produtos){
	var self = this;
	ko.mapping.fromJS(dados, {}, self);
}

Acima temos uma classe com o nome “ProdutoItemCarrinho”, que tem 3 parametros no construtor.
* dados: Irá receber os dados pertinentes a cada item do carrinho.
* produtosSelecionados: Todos os produtos já adicionados.
* produtos: Todos produtos disponíveis

Agora que já temos a classe de porduto para o item do carrinho, podemos voltar a implementação do método: “AdicionarProduto” da classe “ViewModel”, que ficará assim:

self.AdicionarProduto = function(){
	self.ProdutosAdicionados.push(new ProdutoItemCarrinho({Quantidade: 1, ProdutoSelecionado: null}, self.ProdutosAdicionados, self.Produtos));
};

Ao adicionar um produto no carrinho, informamos um objeto javascript com: Quantidade (por padrão 1) e ProdutoSelecionado (para controlar o produto atual dos itens adicionados), também passamos como parametro os produtos já adicionados “ProdutosAdicionados” e por último todos os produtos disponíveis.

Voltando a classe “ProdutoItemCarrinho”, vamos adicionar uma propriedade computada que será responsavel por disponibilizar o valor do Id, do produto selecionado, isso não seria necessário caso utilizássemos a opção “optionsValue” com valor da propriedade “Id”, mas como não queremos apenas o Id do produto, deixamos o KO passar a instancia inteira do produto (que é o comportamento padrão).

function ProdutoItemCarrinho(dados, produtosSelecionados, produtos){
	var self = this;
	ko.mapping.fromJS(dados, {}, self);
	
	self.ProdutoId = ko.computed(function () {
            var produto = ko.unwrap(self.ProdutoSelecionado);
            if (produto)
                return produto.Id;

        return null;
    });
}

Na propriedade computada “ProdutoId” verificamos se há um produto selecionado, caso sim, retornamos o Id, caso contrário retornamos null;
Agora vem a parte um pouco mais complicada, que é a verificação se o produto ja foi adicionado, como segue:

function ProdutoItemCarrinho(dados, produtosSelecionados, produtos){
	var self = this;
	ko.mapping.fromJS(dados, {}, self);
	
	self.ProdutoId = ko.computed(function () {
        var produto = ko.unwrap(self.ProdutoSelecionado);
        if (produto)
            return produto.Id;

        return null;
    });
	
	self.ProdutosDisponiveis = ko.computed(function () {
        var selecionados = ko.unwrap(produtosSelecionados);
        if (selecionados.length == 0)
            return produtos;

        var itensAdicionados = [];
        var itensDisponiveis = [];
        ko.utils.arrayForEach(produtos, function (produto) {
            ko.utils.arrayForEach(ko.unwrap(produtosSelecionados), function (produtoSelecionado) {
				if (produto.Id == ko.unwrap(produtoSelecionado.ProdutoId) && self.ProdutoId() != produto.Id) {
                    itensAdicionados.push(produto.Id);
                }
            });
        });

        ko.utils.arrayForEach(produtos, function (produto) {
            if (itensAdicionados.indexOf(produto.Id) == -1)
                itensDisponiveis.push(produto);
        });

        return ko.unwrap(itensDisponiveis);
    });
}

A propriedade “ProdutosDisponiveis” tem como papel disponibilizar os produtos que não estão adicionados; Primeiro verificamos se há um produto selecionado, caso não tenha, todos os produtos são disponibilizados, do contrário, percorremos cada produto disponivel para cada produto selecionado, caso o produto não seja ele próprio e esteja na lista de produtos selecionados é apendado no array “itensAdicionados”, e por último percorremos todos os itens de “itensAdicionados” verificando se o produto está na lista, caso não esteja, disponibilizamos para mostrar para o interface. Nesse ponto devemos mudar a tag , para que faça uso dessa propriedade:

<select data-bind="options: ProdutosDisponiveis, optionsText: 'Nome', value: ProdutoSelecionado"></select>

* options: Produtos disponíveis.
* optionsText: Valor que aparecerá no text da tag
* ProdutoSelecionado: Observable que ficará encarregado de armazenar o produto selecionado.

Tamos devemos modificar a tag input para armazenar a quantidade

<input type="text" class="form-control input-small" data-bind="value: Quantidade, valueUpdate: 'afterkeydown'" />

Informamos para o value qual propriedade observable usar e que o valor da mesma deve ser atualizado ao evento “afterkeydown”.
Vamos adicionar uma propriedade computada para calcular o sub total do produto, na classe “ProdutoItemCarrinho”

self.SubTotal = ko.computed(function () {
	var produto = ko.unwrap(self.ProdutoSelecionado);
	        if(produto)
		       return produto.Valor * self.Quantidade();
	
	return 0;
});

Primeiro verificamos se há um produto selecionado, caso sim, multiplicamos o valor do produto com a quantidade do item, como não fizemos mapping de produto, a propriedade “Valor” não se tornou “observable”, o que não é o caso de “Quantidade”. Assim finalizamos a classe “ProdutoItemCarrinho”.
Agora podemos voltar a classe “ViewModel” e adicionar uma propriedade computada para verificar se o número de produtos excede a quantidade disponíveis.

self.HabilitadoBotaoVisivel = ko.computed(function () {
	return self.ProdutosAdicionados().length < self.Produtos.length;
});

Acima apenas verificamos se quantidade de produtos adicionados é menor que a quantidade de produtos disponíveis.
E por último na classe “ViewModel”, devemos adicionar outra propriedade computada para calcular o valor total do carrinho.

self.Total = ko.computed(function () {
	var valorTotal = 0;
	ko.utils.arrayForEach(self.ProdutosAdicionados(), function (produto) {
		if (produto.ProdutoSelecionado()){
			valorTotal += produto.SubTotal();
		}
	});

	return valorTotal;
});

Acima apenas percorremos cada item de produto adicionado e somamos o valor sub total, para retornar o total no final, caso não tenha produtos adicionados o valor é 0 (zero).

E para finalizar o javascript devemos implementar um bind handler do KO, para ser possível formatar os dados monetários.

ko.bindingHandlers.textMoney = {
    update: function (element, valueAccessor) {
        var valor = ko.utils.unwrapObservable(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
			valorFlutuante = 0;
        
		$(element).text("R$ " + valorFlutuante.toFixed(2));
    }
};

Acima verificamos se há valor (que seja possível uma conversão númerica) na propriedade que se está passando, para para retornar um número com notação de duas casas decimais. Deixaremos o html dessa forma:

<table class="table table-striped table-condensed table-produtos-cobranca">
	<thead> 
		<tr>
			<th>Produto</th>
			<th>Sub total</th>
			<th>Quantidade</th>
			<th></th>
		</tr>
	</thead>
	<tbody data-bind="foreach: ProdutosAdicionados">
		<tr>
			<td>
				<select data-bind="options: ProdutosDisponiveis, optionsText: 'Nome', value: ProdutoSelecionado"></select>
			</td>
			<td>
				<input type="text" class="form-control input-small" data-bind="value: Quantidade, valueUpdate: 'afterkeydown'" />
			</td>
			<td>
				<span data-bind="textMoney: SubTotal"></span>
			</td>
			<td>
				<button class="btn btn-danger" type="button" data-bind="click: $root.RemoverProduto">Remover</button>
			</td>
		</tr>
	</tbody>
	<tfoot>
		<tr>
			<td colspan="4" class="padding-zero">
				<div class="right">
					<b>Total</b>
					<span data-bind="textMoney: Total"></span>
				</div>
			</td>
		</tr>
		<tr>
			<td colspan="4" class="padding-zero">
				<button class="btn btn-primary margin-top-10" type="button" data-bind="click: AdicionarProduto, enable: HabilitadoBotaoVisivel">Adicionar</button>
			</td>
		</tr>
	</tfoot>
</table>

Acima adicionamos a tag span na coluna do sub total, que utiliza a propriedade “SubTotal”; Também adicionamos uma nova linha na tag tfoot para mostrar o valor total do carrinho, e modificamos o botão de adicionar para que fique desabilitado caso a quantidade de produtos disponíveis esteja esgotada. Criei um post maior dessa vez, na intenção de detalhar os passos de desenvolvimento.

Espero que tenha ajudado!