Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
de Rick Anderson
Crear la página principal
En esta sección, creará la página principal de la aplicación. Esta página será más compleja que la página Administrador, por lo que la abordaremos en varios pasos. A lo largo del proceso, verá algunas técnicas de Knockout.js más avanzadas. Este es el diseño básico de la página:
Diagrama de interacción entre productos, carritos, pedidos y detalles de los pedidos en una página principal. El elemento "products" está etiquetado como "GET A P I / products" y tiene una flecha que apunta al elemento "items". El elemento items está conectado al elemento orders por una flecha etiquetada POST A P I / orders. El elemento orders está conectado al elemento details mediante una flecha etiquetada como GET A P I / orders. El elemento details está etiquetado como GET A P I / orders / i d.
- "Productos" contiene una matriz de productos.
- "Cart" contiene una matriz de productos con cantidades. Al hacer clic en "Agregar al carro" se actualiza el carro.
- "Pedidos" contiene un array de IDs de pedidos.
- "Detalles" contiene un detalle del pedido, que es una matriz de artículos (productos con cantidades)
Empezaremos definiendo algún diseño básico en HTML, sin enlace de datos ni script. Abra el archivo Views/Home/Index.cshtml y reemplace todo el contenido por lo siguiente:
<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>
A continuación, agregue una sección Scripts y cree un modelo de vista vacío:
@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>
}
En función del boceto del diseño realizado anteriormente, nuestro modelo de vista necesita observables para productos, carro de compras, pedidos y detalles. Agregue las siguientes variables al AppViewModel objeto :
self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();
Los usuarios pueden agregar elementos de la lista de productos al carro y quitar elementos del carro. Para encapsular estas funciones, crearemos otra clase de modelo de vista que represente un producto. Agrega el código siguiente a 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);
};
}
}
La ProductViewModel clase contiene dos funciones que se usan para mover el producto hacia y desde el carro: addItemToCart agrega una unidad del producto al carro y removeAllFromCart quita todas las cantidades del producto.
Los usuarios pueden seleccionar un pedido existente y obtener los detalles del pedido. Encapsularemos esta funcionalidad en otro modelo de vista:
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);
})
});
};
}
OrderDetailsViewModel se inicializa con un pedido y captura los detalles del pedido mediante el envío de una solicitud AJAX al servidor.
Además, observe la total propiedad en el OrderDetailsViewModel. Esta propiedad es un tipo especial de observable denominado observable calculado. Como indica el nombre, un observable calculado permite enlazar datos a un valor calculado; en este caso, el costo total del pedido.
A continuación, agregue estas funciones a AppViewModel:
-
resetCartquita todos los elementos de la cesta. -
getDetailsobtiene los detalles de un pedido (insertando un nuevoOrderDetailsViewModelen ladetailslista). -
createOrdercrea un nuevo pedido y vacía el carro.
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);
}
});
};
};
Por último, inicialice el modelo de vista realizando solicitudes AJAX para los productos y pedidos:
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, eso es un montón de código, pero lo compilamos paso a paso, así que esperamos que el diseño esté claro. Ahora podemos agregar algunos enlaces de Knockout.js al código HTML.
Productos
Estos son las vinculaciones de la lista de productos:
<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>
Esto recorre en iteración la matriz de productos y muestra el nombre y el precio. El botón "Agregar al pedido" solo está visible cuando el usuario ha iniciado sesión.
El botón "Agregar al pedido" llama a addItemToCart en la instancia del ProductViewModel para el producto. Esto demuestra una buena característica de Knockout.js: cuando un view-model contiene otros view-models, se pueden aplicar los enlaces al modelo interno. En este ejemplo, los enlaces dentro de foreach se aplican a cada una de las instancias de ProductViewModel. Este enfoque es mucho más limpio que colocar toda la funcionalidad en un único modelo de vista.
Carro
Estas son las vinculaciones para el carrito:
<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"/>
Esto recorre en iteración la matriz del carro y muestra el nombre, el precio y la cantidad. Tenga en cuenta que el vínculo "Quitar" y el botón "Crear pedido" están enlazados a funciones de modelo de vista.
Pedidos
Estos son las vinculaciones de la lista de órdenes:
<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>
Esto recorre en iteración los pedidos y muestra el identificador de pedido. El evento click del vínculo está enlazado a la getDetails función .
Detalles del pedido
Estos son las vinculaciones para los detalles del pedido:
<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>
Esto recorre en iteración los artículos en el pedido y muestra el producto, el precio y la cantidad. El div circundante solo está visible si la matriz de detalles contiene uno o varios elementos.
Conclusión
En este tutorial, ha creado una aplicación que usa Entity Framework para comunicarse con la base de datos y ASP.NET API web para proporcionar una interfaz orientada al público sobre la capa de datos. Usamos ASP.NET MVC 4 para representar las páginas HTML y Knockout.js más jQuery para proporcionar interacciones dinámicas sin recargas de página.
Recursos adicionales: