7 부: 기본 페이지 만들기Part 7: Creating the Main Page

Mike Wassonby Mike Wasson

완료 된 프로젝트 다운로드Download Completed Project

기본 페이지 만들기Creating the Main Page

이 섹션에서는 주 응용 프로그램 페이지를 만듭니다.In this section, you will create the main application page. 이 페이지는 관리 페이지 보다 복잡 하므로 여러 단계로 접근 합니다.This page will be more complex than the Admin page, so we'll approach it in several steps. 이 과정에서 몇 가지 고급에 대 한 몇 가지 고급 녹아웃 기술이 표시 됩니다.Along the way, you'll see some more advanced Knockout.js techniques. 페이지의 기본 레이아웃은 다음과 같습니다.Here is the basic layout of the page:

  • "Products"는 제품 배열을 보유 합니다."Products" holds an array of products.
  • "카트"는 수량이 포함 된 제품의 배열을 보유 합니다."Cart" holds an array of products with quantities. "카트에 추가"를 클릭 하면 카트를 업데이트 합니다.Clicking "Add to Cart" updates the cart.
  • "Orders"는 주문 Id의 배열을 보유 합니다."Orders" holds an array of order IDs.
  • "세부 정보"는 항목 배열 (수량이 있는 제품) 인 주문 세부 정보를 포함 합니다."Details" holds an order detail, which is an array of items (products with quantities)

데이터 바인딩이나 스크립트 없이 HTML에서 몇 가지 기본 레이아웃을 정의 하는 것부터 시작 합니다.We'll start by defining some basic layout in HTML, with no data binding or script. 파일 Views/Home/Index를 열고 모든 내용을 다음으로 바꿉니다.Open the file Views/Home/Index.cshtml and replace all of the contents with the following:

<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>

다음으로 스크립트 섹션을 추가 하 고 빈 뷰-모델을 만듭니다.Next, add a Scripts section and create an empty view-model:

@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>
}

이전에 스케치 된 디자인을 기반으로 하는 보기 모델에는 제품, 카트, 주문 및 세부 정보에 대 한 관찰 가능 개체 필요 합니다.Based on the design sketched earlier, our view model needs observables for products, cart, orders, and details. AppViewModel 개체에 다음 변수를 추가 합니다.Add the following variables to the AppViewModel object:

self.products = ko.observableArray();
self.cart = ko.observableArray();
self.orders = ko.observableArray();
self.details = ko.observable();

사용자는 제품 목록의 항목을 카트에 추가 하 고 카트에 항목을 제거할 수 있습니다.Users can add items from the products list into the cart, and remove items from the cart. 이러한 함수를 캡슐화 하기 위해 제품을 나타내는 또 다른 뷰 모델 클래스를 만듭니다.To encapsulate these functions, we'll create another view-model class that represents a product. 다음 코드를 AppViewModel에 추가합니다.Add the following code to 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 제품의 모든 수량을 제거 합니다.The ProductViewModel class contains two functions that are used to move the product to and from the cart: addItemToCart adds one unit of the product to the cart, and removeAllFromCart removes all quantities of the product.

사용자는 기존 주문을 선택 하 고 주문 세부 정보를 가져올 수 있습니다.Users can select an existing order and get the order details. 이 기능은 다른 보기-모델에 캡슐화 됩니다.We'll encapsulate this functionality into another view-model:

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 요청을 전송 하 여 주문 정보를 페치합니다.The OrderDetailsViewModel is initialized with an order, and it fetches the order details by sending an AJAX request to the server.

또한 OrderDetailsViewModeltotal 속성이 있는지 확인 합니다.Also, notice the total property on the OrderDetailsViewModel. 이 속성은 계산 된 관찰가능 이라고 하는 특수 한 종류의 관찰 가능입니다.This property is a special kind of observable called a computed observable. 이름에서 알 수 있듯이 계산 된 관찰 가능을 통해 계산 된 값—(이 경우 주문의 총 비용)에 데이터를 바인딩할 수 있습니다.As the name implies, a computed observable lets you data bind to a computed value—in this case, the total cost of the order.

그런 다음 AppViewModel에 다음 함수를 추가 합니다.Next, add these functions to AppViewModel:

  • resetCart 카트에 있는 모든 항목을 제거 합니다.resetCart removes all items from the cart.
  • getDetails는 주문에 대 한 세부 정보를 가져옵니다 (details 목록에 새 OrderDetailsViewModel 푸시).getDetails gets the details for an order (by pushing a new OrderDetailsViewModel onto the details list).
  • createOrder 새 주문을 만들고 카트를 비웁니다.createOrder creates a new order and empties the cart.
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 요청을 작성 하 여 뷰 모델을 초기화 합니다.Finally, initialize the view model by making AJAX requests for the products and orders:

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, that's a lot of code, but we built it up step-by-step, so hopefully the design is clear. 이제 HTML에 일부 녹아웃 바인딩을 추가할 수 있습니다.Now we can add some Knockout.js bindings to the HTML.

제품Products

제품 목록에 대 한 바인딩은 다음과 같습니다.Here are the bindings for the product list:

<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>

이는 products 배열을 반복 하 고 이름 및 가격을 표시 합니다.This iterates over the products array and displays the name and price. "주문에 추가" 단추는 사용자가 로그인 한 경우에만 표시 됩니다.The "Add to Order" button is visible only when the user is logged in.

"주문에 추가" 단추는 제품의 ProductViewModel 인스턴스에서 addItemToCart를 호출 합니다.The "Add to Order" button calls addItemToCart on the ProductViewModel instance for the product. 이는 녹아웃의 유용한 기능을 보여 줍니다. 뷰 모델에 다른 뷰 모델이 포함 되어 있으면 바인딩을 내부 모델에 적용할 수 있습니다.This demonstrates a nice feature of Knockout.js: When a view-model contains other view-models, you can apply the bindings to the inner model. 이 예에서는 foreach 내의 바인딩이 각 ProductViewModel 인스턴스에 적용 됩니다.In this example, the bindings within the foreach are applied to each of the ProductViewModel instances. 이 방법은 모든 기능을 단일 뷰 모델에 배치 하는 것 보다 훨씬 더 간단 합니다.This approach is much cleaner than putting all of the functionality into a single view-model.

마차Cart

카트에 대 한 바인딩은 다음과 같습니다.Here are the bindings for the cart:

<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"/>

그러면 카트 배열을 반복 하 고 이름, 가격 및 수량을 표시 합니다.This iterates over the cart array and displays the name, price, and quantity. "제거" 링크와 "주문 만들기" 단추는 뷰 모델 함수에 바인딩되어 있습니다.Note that the "Remove" link and the "Create Order" button are bound to view-model functions.

주문과Orders

주문 목록에 대 한 바인딩은 다음과 같습니다.Here are the bindings for the orders list:

<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를 표시 합니다.This iterates over the orders and shows the order ID. 링크의 click 이벤트는 getDetails 함수에 바인딩됩니다.The click event on the link is bound to the getDetails function.

주문 정보Order Details

주문 정보에 대 한 바인딩은 다음과 같습니다.Here are the bindings for the 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>

그러면 주문의 항목을 반복 하 여 제품, 가격 및 수량을 표시 합니다.This iterates over the items in the order and displays the product, price, and quantity. 주변 div는 세부 정보 배열에 하나 이상의 항목이 포함 된 경우에만 표시 됩니다.The surrounding div is visible only if the details array contains one or more items.

결론Conclusion

이 자습서에서는 Entity Framework를 사용 하 여 데이터베이스와 통신 하는 응용 프로그램을 만들고 ASP.NET Web API 데이터 계층 위에 공용 인터페이스를 제공 합니다.In this tutorial, you created an application that uses Entity Framework to communicate with the database, and ASP.NET Web API to provide a public-facing interface on top of the data layer. ASP.NET MVC 4를 사용 하 여 HTML 페이지를 렌더링 하 고, .js와 jQuery를 사용 하 여 페이지를 다시 로드 하지 않고 동적 상호 작용을 제공 합니다.We use ASP.NET MVC 4 to render the HTML pages, and Knockout.js plus jQuery to provide dynamic interactions without page reloads.

추가 리소스:Additional resources: