7부: 기본 페이지 만들기
작성자: Rick Anderson
기본 페이지 만들기
이 섹션에서는 기본 애플리케이션 페이지를 만듭니다. 이 페이지는 관리 페이지보다 더 복잡하므로 몇 가지 단계로 접근하겠습니다. 그 과정에서 몇 가지 고급 Knockout.js 기술을 볼 수 있습니다. 페이지의 기본 레이아웃은 다음과 같습니다.
기본 페이지의 제품, 카트, 주문 및 주문 세부 정보 요소 간의 상호 작용 다이어그램 products 요소에는 항목 요소를 가리키는 화살표가 있는 GET A P I/products 레이블이 지정됩니다. items 요소는 POST A P I/orders라는 화살표로 orders 요소에 연결됩니다. orders 요소는 GET A P I/orders라는 레이블이 지정된 화살표를 사용하여 details 요소에 연결됩니다. details 요소는 GET A P I / orders / i d로 레이블이 지정됩니다.
- "제품"에는 다양한 제품이 있습니다.
- "카트"는 수량이 있는 제품의 배열을 보유합니다. "카트에 추가"를 클릭하면 카트가 업데이트됩니다.
- "Orders"는 주문 ID의 배열을 보유합니다.
- "세부 정보"에는 항목 배열(수량이 있는 제품)인 주문 세부 정보가 포함됩니다.
먼저 데이터 바인딩이나 스크립트 없이 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);
};
좋아, 그건 많은 코드, 하지만 우리는 단계별로 그것을 구축, 그래서 잘하면 디자인은 분명. 이제 HTML에 몇 가지 Knockout.js 바인딩을 추가할 수 있습니다.
제품
제품 목록에 대한 바인딩은 다음과 같습니다.
<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>
제품 배열을 반복하고 이름과 가격을 표시합니다. "주문에 추가" 단추는 사용자가 로그인한 경우에만 표시됩니다.
제품에 대한 instance "주문에 ProductViewModel
추가" 단추가 호출 addItemToCart
됩니다. 이는 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>
이는 주문을 반복하고 주문 ID를 표시합니다. 링크의 클릭 이벤트는 함수에 getDetails
바인딩됩니다.
Order Details
다음은 주문 세부 정보에 대한 바인딩입니다.
<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는 세부 정보 배열에 하나 이상의 항목이 포함된 경우에만 표시됩니다.
결론
이 자습서에서는 Entity Framework를 사용하여 데이터베이스와 통신하고 ASP.NET Web API 데이터 계층 위에 공용 인터페이스를 제공하는 애플리케이션을 만들었습니다. ASP.NET MVC 4를 사용하여 HTML 페이지를 렌더링하고 Knockout.js 더하기 jQuery를 사용하여 페이지 다시 로드 없이 동적 상호 작용을 제공합니다.
추가 리소스: