Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
por Rick Anderson
Criação da Página Principal
Nesta secção, irá criar a página principal da candidatura. Esta página será mais complexa do que a página de Administração, por isso vamos abordá-la em vários passos. Pelo caminho, vais ver técnicas de Knockout.js mais avançadas. Aqui está o layout básico da página:
Diagrama da interação entre produtos, carrinho, encomendas e elementos de detalhes de encomenda de uma página principal. O elemento products está rotulado GET A P I / products, com uma seta que aponta para o elemento items. O elemento itens está ligado ao elemento encomendas por uma seta rotulada POST A P I / encomendas. O elemento encomendas está ligado ao elemento detalhes com uma seta rotulada GET A P I / encomendas. O elemento de detalhes é lebelado GET A P I / orders / i d.
- "Produtos" contém uma variedade de produtos.
- "Carrinho" contém uma variedade de produtos com quantidades. Clicar em "Adicionar ao carrinho" atualiza o carrinho.
- "Orders" contém uma matriz de IDs de encomenda.
- "Detalhes" contém um detalhe de encomenda, que é uma lista de itens (produtos com quantidades)
Vamos começar por definir um layout básico em HTML, sem ligação de dados ou script. Abra o ficheiro Views/Home/Index.cshtml e substitua todo o conteúdo pelo seguinte:
<div class="content">
<!-- List of products -->
<div class="float-left">
<h1>Products</h1>
<ul id="products">
</ul>
</div>
<!-- Cart -->
<div id="cart" class="float-right">
<h1>Your Cart</h1>
<table class="details ui-widget-content">
</table>
<input type="button" value="Create Order"/>
</div>
</div>
<div id="orders-area" class="content" >
<!-- List of orders -->
<div class="float-left">
<h1>Your Orders</h1>
<ul id="orders">
</ul>
</div>
<!-- Order Details -->
<div id="order-details" class="float-right">
<h2>Order #<span></span></h2>
<table class="details ui-widget-content">
</table>
<p>Total: <span></span></p>
</div>
</div>
De seguida, adicione uma secção de Scripts e crie um modelo de visualização vazio:
@section Scripts {
<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.1.0.js")"></script>
<script type="text/javascript">
function AppViewModel() {
var self = this;
self.loggedIn = @(Request.IsAuthenticated ? "true" : "false");
}
$(document).ready(function () {
ko.applyBindings(new AppViewModel());
});
</script>
}
Com base no design esboçado anteriormente, o nosso modelo de visualização precisa de observáveis para produtos, carrinho, encomendas e detalhes. Adicione as seguintes variáveis ao AppViewModel objeto:
self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();
Os utilizadores podem adicionar artigos da lista de produtos ao carrinho e remover artigos do carrinho. Para encapsular estas funções, vamos criar outra classe de modelo de vista que representa um produto. Adicione o seguinte código ao AppViewModel:
function AppViewModel() {
// ...
// NEW CODE
function ProductViewModel(root, product) {
var self = this;
self.ProductId = product.Id;
self.Name = product.Name;
self.Price = product.Price;
self.Quantity = ko.observable(0);
self.addItemToCart = function () {
var qty = self.Quantity();
if (qty == 0) {
root.cart.push(self);
}
self.Quantity(qty + 1);
};
self.removeAllFromCart = function () {
self.Quantity(0);
root.cart.remove(self);
};
}
}
A ProductViewModel classe contém duas funções que são usadas para mover o produto para e a partir do carrinho: addItemToCart adiciona uma unidade do produto ao carrinho e removeAllFromCart remove todas as quantidades do produto.
Os utilizadores podem selecionar uma encomenda existente e obter os detalhes da encomenda. Vamos encapsular esta funcionalidade noutro modelo de visualização:
function AppViewModel() {
// ...
// NEW CODE
function OrderDetailsViewModel(order) {
var self = this;
self.items = ko.observableArray();
self.Id = order.Id;
self.total = ko.computed(function () {
var sum = 0;
$.each(self.items(), function (index, item) {
sum += item.Price * item.Quantity;
});
return '$' + sum.toFixed(2);
});
$.getJSON("/api/orders/" + order.Id, function (order) {
$.each(order.Details, function (index, item) {
self.items.push(item);
})
});
};
}
O OrderDetailsViewModel é inicializado com uma ordem e obtém os detalhes da encomenda enviando um pedido AJAX ao servidor.
Além disso, observe a total propriedade no OrderDetailsViewModel. Esta propriedade é um tipo especial de observável chamado observável calculado. Como o nome indica, um observável calculado permite-lhe ligar dados a um valor calculado — neste caso, o custo total da encomenda.
De seguida, adicione estas funções a AppViewModel:
-
resetCartremove todos os artigos do carrinho. -
getDetailsobtém os detalhes de uma encomenda (colocando uma novaOrderDetailsViewModelnadetailslista). -
createOrdercria uma nova ordem e esvazia o carrinho.
function AppViewModel() {
// ...
// NEW CODE
self.resetCart = function() {
var items = self.cart.removeAll();
$.each(items, function (index, product) {
product.Quantity(0);
});
}
self.getDetails = function (order) {
self.details(new OrderDetailsViewModel(order));
}
self.createOrder = function () {
var jqxhr = $.ajax({
type: 'POST',
url: "api/orders",
contentType: 'application/json; charset=utf-8',
data: ko.toJSON({ Details: self.cart }),
dataType: "json",
success: function (newOrder) {
self.resetCart();
self.orders.push(newOrder);
},
error: function (jqXHR, textStatus, errorThrown) {
self.errorMessage(errorThrown);
}
});
};
};
Finalmente, inicialize o modelo de visualização fazendo pedidos AJAX para os produtos e encomendas:
function AppViewModel() {
// ...
// NEW CODE
// Initialize the view-model.
$.getJSON("/api/products", function (products) {
$.each(products, function (index, product) {
self.products.push(new ProductViewModel(self, product));
})
});
$.getJSON("api/orders", self.orders);
};
OK, é muito código, mas fomos construindo passo a passo, por isso esperamos que o design esteja claro. Agora podemos adicionar algumas ligações Knockout.js ao HTML.
Produtos
Aqui estão as associações para a lista de produtos:
<ul id="products" data-bind="foreach: products">
<li>
<div>
<span data-bind="text: Name"></span>
<span class="price" data-bind="text: '$' + Price"></span>
</div>
<div data-bind="if: $parent.loggedIn">
<button data-bind="click: addItemToCart">Add to Order</button>
</div>
</li>
</ul>
Isto itera sobre a lista de produtos e mostra o nome e o preço. O botão "Adicionar à Encomenda" só é visível quando o utilizador está iniciado sessão.
O botão "Adicionar à Encomenda" chama addItemToCart na instância ProductViewModel do produto. Isto demonstra uma característica interessante do Knockout.js: quando um modelo de vista contém outros modelos de visualização, pode aplicar as ligações ao modelo interno. Neste exemplo, as ligações dentro do foreach são aplicadas a cada uma das ProductViewModel instâncias. Esta abordagem é muito mais limpa do que colocar toda a funcionalidade num único modelo de visualização.
Carrinho
Aqui estão as fixações para o carrinho:
<div id="cart" class="float-right" data-bind="visible: cart().length > 0">
<h1>Your Cart</h1>
<table class="details ui-widget-content">
<thead>
<tr><td>Item</td><td>Price</td><td>Quantity</td><td></td></tr>
</thead>
<tbody data-bind="foreach: cart">
<tr>
<td><span data-bind="text: $data.Name"></span></td>
<td>$<span data-bind="text: $data.Price"></span></td>
<td class="qty"><span data-bind="text: $data.Quantity()"></span></td>
<td><a href="#" data-bind="click: removeAllFromCart">Remove</a></td>
</tr>
</tbody>
</table>
<input type="button" data-bind="click: createOrder" value="Create Order"/>
A função percorre o array do carrinho e mostra o nome, preço e quantidade. Note que o link "Remover" e o botão "Criar Encomenda" estão associados ao modelo de visualização.
Encomendas
Aqui estão as ligações para a lista de encomendas:
<h1>Your Orders</h1>
<ul id="orders" data-bind="foreach: orders">
<li class="ui-widget-content">
<a href="#" data-bind="click: $root.getDetails">
Order # <span data-bind="text: $data.Id"></span></a>
</li>
</ul>
Isto itera sobre as encomendas e mostra o identificador da encomenda. O evento de clique no link está ligado à getDetails função.
Detalhes da Encomenda
Aqui estão as encadernações para os detalhes da encomenda:
<div id="order-details" class="float-right" data-bind="if: details()">
<h2>Order #<span data-bind="text: details().Id"></span></h2>
<table class="details ui-widget-content">
<thead>
<tr><td>Item</td><td>Price</td><td>Quantity</td><td>Subtotal</td></tr>
</thead>
<tbody data-bind="foreach: details().items">
<tr>
<td><span data-bind="text: $data.Product"></span></td>
<td><span data-bind="text: $data.Price"></span></td>
<td><span data-bind="text: $data.Quantity"></span></td>
<td>
<span data-bind="text: ($data.Price * $data.Quantity).toFixed(2)"></span>
</td>
</tr>
</tbody>
</table>
<p>Total: <span data-bind="text: details().total"></span></p>
</div>
Isto itera sobre os itens da encomenda e mostra o produto, preço e quantidade. A div circundante só é visível se o array de detalhes contiver um ou mais itens.
Conclusion
Neste tutorial, criou uma aplicação que utiliza o Entity Framework para comunicar com a base de dados e ASP.NET API Web para fornecer uma interface pública sobre a camada de dados. Usamos ASP.NET MVC 4 para renderizar as páginas HTML, e Knockout.js mais jQuery para fornecer interações dinâmicas sem recargas de página.
Recursos adicionais: