Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Autor: Rick Anderson
Pobieranie ukończonego projektu
Tworzenie strony głównej
W tej sekcji utworzysz stronę główną aplikacji. Ta strona będzie bardziej złożona niż strona administratora, więc zajmiemy się nią w kilku krokach. Po drodze zobaczysz kilka bardziej zaawansowanych technik Knockout.js. Oto podstawowy układ strony:
Diagram interakcji między produktami, koszykiem, zamówieniami i elementami szczegółów zamówienia strony głównej. Element products jest oznaczony jako GET API / products, a strzałka wskazuje na element items. Element items jest połączony z elementem orders za pomocą strzałki oznaczonej POST API / orders. Element orders jest połączony z elementem details za pomocą strzałki z etykietą GET API / orders. Element szczegółów jest oznaczony GET A P I / orders / i d.
- "Produkty" zawiera tablicę produktów.
- "Koszyk" zawiera tablicę produktów z kwantyfikatorami. Kliknięcie pozycji "Dodaj do koszyka" aktualizuje koszyk.
- "Orders" zawiera tablicę identyfikatorów zamówień.
- "Szczegóły" zawiera szczegóły zamówienia, czyli tablicę elementów (produktów z ilościami)
Zaczniemy od zdefiniowania podstawowego układu w języku HTML bez powiązania danych ani skryptu. Otwórz plik Views/Home/Index.cshtml i zastąp całą zawartość następującym kodem:
<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>
Następnie dodaj sekcję Skrypty i utwórz pusty model widoku:
@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>
}
Na podstawie naszkicowanego wcześniej projektu nasz model widoku potrzebuje obserwowalnych elementów dla produktów, koszyka, zamówień i szczegółów. Dodaj następujące zmienne do AppViewModel obiektu:
self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();
Użytkownicy mogą dodawać elementy z listy produktów do koszyka i usuwać elementy z koszyka. Aby hermetyzować te funkcje, utworzymy kolejną klasę modelu widoków reprezentującą produkt. Dodaj następujący kod do 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);
};
}
}
Klasa ProductViewModel zawiera dwie funkcje używane do przenoszenia produktu do i z koszyka: addItemToCart dodaje jedną jednostkę produktu do koszyka i removeAllFromCart usuwa wszystkie ilości produktu.
Użytkownicy mogą wybrać istniejące zamówienie i uzyskać szczegóły zamówienia. Hermetyzujemy tę funkcję do innego modelu widoków:
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);
})
});
};
}
Element OrderDetailsViewModel jest inicjowany przy użyciu zamówienia i pobiera szczegóły zamówienia, wysyłając żądanie AJAX do serwera.
Zwróć również uwagę na total właściwość na OrderDetailsViewModel. Ta właściwość to specjalny rodzaj obserwowalnej zwanej obserwowalną obliczaną. Jak wskazuje nazwa, obliczana zmienna umożliwia wiązanie danych z obliczoną wartością — w tym przypadku łączny koszt zamówienia.
Następnie dodaj następujące funkcje do elementu AppViewModel:
-
resetCartusuwa wszystkie elementy z koszyka. -
getDetailspobiera szczegóły zamówienia (dodając nowąOrderDetailsViewModeldo listydetails). -
createOrdertworzy nowe zamówienie i opróżnia koszyk.
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);
}
});
};
};
Na koniec zainicjuj model widoku, wysyłając żądania AJAX dotyczące produktów i zamówień:
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, to dużo kodu, ale utworzyliśmy go krok po kroku, więc miejmy nadzieję, że projekt jest jasny. Teraz możemy dodać pewne powiązania Knockout.js do kodu HTML.
Produkty
Poniżej przedstawiono powiązania listy produktów:
<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>
Ten kod iteruje przez tablicę produktów i wyświetla nazwę oraz cenę. Przycisk "Dodaj do zamówienia" jest widoczny tylko wtedy, gdy użytkownik jest zalogowany.
Przycisk "Dodaj do zamówienia" wywołuje addItemToCart na instancji ProductViewModel dla produktu. Pokazuje to przydatną funkcję Knockout.js: gdy model-widoku zawiera inne modele widoków, można zastosować powiązania na wewnętrznym modelu. W tym przykładzie powiązania w ramach foreach są stosowane do każdego z wystąpień ProductViewModel. Takie podejście jest znacznie czystsze niż umieszczenie wszystkich funkcji w jednym modelu widoków.
Koszyk
Oto wiązania koszyka:
<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"/>
Ta iteruje po tablicy koszyka i wyświetla nazwę, cenę i ilość. Należy pamiętać, że link "Usuń" i przycisk "Utwórz zamówienie" są powiązane z funkcjami widoku modelu.
Zamówienia
Oto ustawienia listy zamówień:
<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>
Spowoduje to iteracje zamówień i wyświetlenie identyfikatora zamówienia. Zdarzenie kliknięcia linku jest powiązane z funkcją getDetails .
Szczegóły zamówienia
Poniżej przedstawiono powiązania szczegółów zamówienia:
<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>
To iteruje przez elementy zamówienia i wyświetla produkt, cenę i ilość. Otaczający element div jest widoczny tylko wtedy, gdy tablica szczegółów zawiera co najmniej jeden element.
Podsumowanie
W tym samouczku utworzono aplikację, która używa Entity Framework do komunikacji z bazą danych, a ASP.NET Web API do udostępnienia interfejsu publicznego nad warstwą danych. Używamy ASP.NET MVC 4 do renderowania stron HTML i Knockout.js plus jQuery w celu zapewnienia dynamicznych interakcji bez ponownego ładowania strony.
Dodatkowe zasoby: