Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Рик Андерсон
Скачивание завершенного проекта
Создание главной страницы
В этом разделе вы создадите главную страницу приложения. Эта страница будет более сложной, чем страница администрирования, поэтому мы рассмотрим ее в нескольких шагах. На этом пути вы увидите более сложные методы Knockout.js. Ниже приведен базовый макет страницы:
Диаграмма взаимодействий продуктов, корзины, заказов и элементов деталей заказа на главной странице. Элемент products помечен как GET A P I / products с стрелкой, указывающей на элемент items. Элемент items подключается к элементу orders стрелкой с меткой POST API /orders. Элемент orders соединен с элементом сведений стрелкой с меткой "GET API / orders". Элемент details помечен как GET A P I / orders / id.
- "Продукты" содержит набор продуктов.
- "Корзина" содержит список товаров вместе с их количеством. При нажатии кнопки "Добавить в корзину" обновляется корзина.
- "Orders" содержит массив идентификаторов заказов.
- "Details" содержит детали заказа, что представляет собой массив позиций (продуктов с количеством).
Начнем с определения базового макета в HTML без привязки данных или скрипта. Откройте файл Views/Home/Index.cshtml и замените все содержимое следующим образом:
<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>
Затем добавьте раздел "Скрипты" и создайте пустую модель представления:
@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>
}
На основе наброска дизайна наша модель представления нуждается в наблюдаемых объектах для продуктов, корзины, заказов и деталей. Добавьте в объект следующие переменные AppViewModel :
self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();
Пользователи могут добавлять элементы из списка продуктов в корзину и удалять элементы из корзины. Чтобы инкапсулировать эти функции, мы создадим другой класс модели представления, представляющий продукт. Добавьте следующий код в 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);
};
}
}
Класс ProductViewModel содержит две функции, которые используются для перемещения продукта в корзину и из нее: addItemToCart добавляет одну единицу продукта в корзину и removeAllFromCart удаляет все количества продукта.
Пользователи могут выбрать существующий заказ и получить сведения о заказе. Мы инкапсулируем эту функцию в другую модель представления:
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 инициализирован заказом и получает сведения о заказе, отправляя AJAX-запрос на сервер.
Кроме того, обратите внимание на свойство total на OrderDetailsViewModel. Это свойство является особым типом наблюдаемого, называемого вычисляемым наблюдаемым. Как следует из названия, вычисляемое наблюдаемое значение позволяет привязать данные к вычисляемому значению, в этом случае — общей стоимости заказа.
Затем добавьте следующие функции в AppViewModel:
-
resetCartУдаляет все элементы из корзины. -
getDetailsполучает сведения о заказе (путем добавления новогоOrderDetailsViewModelв списокdetails). -
createOrderсоздает новый заказ и очищает корзину.
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);
}
});
};
};
Наконец, инициализировать модель представления, выполнив запросы AJAX для продуктов и заказов:
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);
};
Хорошо, это большое количество кода, но мы создавали его пошагово, так что, надеюсь, замысел ясен. Теперь можно добавить некоторые Knockout.js привязки к HTML.
Продукция
Ниже приведены привязки для списка продуктов:
<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>
Это выполняет итерацию по массиву продуктов и отображает имя и цену. Кнопка "Добавить в заказ" отображается только в том случае, если пользователь вошел в систему.
Кнопка "Добавить в заказ" вызывает функцию addItemToCart на экземпляре ProductViewModel для продукта. Это демонстрирует хорошую функцию Knockout.js: если модель представления содержит другие модели представления, можно применить привязки к внутренней модели. В этом примере привязки в пределах объекта foreach применяются к каждому экземпляру ProductViewModel . Такой подход гораздо более чистый, чем размещение всех функциональных возможностей в одной модели представления.
Корзину
Ниже приведены привязки для корзины:
<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"/>
Это выполняет итерацию по массиву корзины и отображает имя, цену и количество. Обратите внимание, что ссылка "Удалить" и кнопка "Создать заказ" привязаны к функциям модели просмотра.
Заказы
Ниже приведены привязки для списка заказов:
<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>
Это выполняет итерацию по заказам и отображает идентификатор заказа. Событие щелчка по ссылке привязано к getDetails функции.
Сведения о заказе
Ниже приведены привязки для сведений о заказе:
<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>
Это выполняет итерацию по элементам в заказе и отображает продукт, цену и количество. Окружающий div отображается только в том случае, если массив сведений содержит один или несколько элементов.
Conclusion
В этом руководстве вы создали приложение, использующее Entity Framework для взаимодействия с базой данных, и ASP.NET веб-API для предоставления общедоступного интерфейса на вершине слоя данных. Мы используем ASP.NET MVC 4 для отрисовки HTML-страниц и Knockout.js плюс jQuery для обеспечения динамических взаимодействий без перезагрузки страниц.
Дополнительные ресурсы: