Freigeben über


Teil 7: Erstellen der Hauptseite

von Rick Anderson

Abgeschlossenes Projekt herunterladen

Erstellen der Hauptseite

In diesem Abschnitt erstellen Sie die Hauptanwendungsseite. Diese Seite ist komplexer als die Administratorseite, sodass wir sie in mehreren Schritten nähern. Unterwegs wirst du einige komplexere Knockout.js-Techniken kennenlernen. Hier ist das grundlegende Layout der Seite:

Diagramm der Interaktion zwischen Produkten, Einkaufswagen, Bestellungen und Bestelldetails elementen einer Hauptseite.

Diagramm der Interaktion zwischen Produkten, Einkaufswagen, Bestellungen und Bestelldetails elementen einer Hauptseite. Das Produktelement ist mit GET API/Produkte bezeichnet, wobei ein Pfeil auf die Elemente zeigt. Das Element "items" ist mit dem Orders-Element durch einen Pfeil mit der Bezeichnung POST A P I /Orders verbunden. Das Orders-Element ist mit dem Detailelement mit einem Pfeil mit der Bezeichnung GET A P I /Orders verbunden. Das Detail-Element ist beschriftet mit GET API / orders / id.

  • "Produkte" enthält eine Sammlung von Produkten.
  • "Warenkorb" enthält eine Reihe von Produkten in bestimmten Mengen. Durch Klicken auf "Zum Warenkorb hinzufügen" wird der Warenkorb aktualisiert.
  • "Orders" enthält ein Array von Bestell-IDs.
  • "Details" enthält eine Auftragsdetailansicht, die als ein Array von Artikeln (Produkte mit Mengenangaben) vorliegt.

Zunächst definieren wir ein einfaches Layout in HTML, ohne Datenbindung oder Skript. Öffnen Sie die Datei Views/Home/Index.cshtml, und ersetzen Sie den gesamten Inhalt durch Folgendes:

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

Fügen Sie als Nächstes einen Abschnitt "Skripts" hinzu, und erstellen Sie ein leeres Ansichtsmodell:

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

Basierend auf dem zuvor skizzierten Design benötigt unser Ansichtsmodell Observables für Produkte, Warenkorb, Bestellungen und Details. Fügen Sie dem AppViewModel Objekt die folgenden Variablen hinzu:

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

Benutzer können Artikel aus der Produktliste in den Warenkorb einfügen und Artikel aus dem Warenkorb entfernen. Um diese Funktionen zu kapseln, erstellen wir eine weitere Ansichtsmodellklasse, die ein Produkt darstellt. Fügen Sie den folgenden Code zu AppViewModel hinzu:

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);
        };
    }
}

Die ProductViewModel Klasse enthält zwei Funktionen, die verwendet werden, um das Produkt in den Warenkorb zu verschieben: addItemToCart Fügt eine Einheit des Produkts zum Warenkorb hinzu und removeAllFromCart entfernt alle Mengen des Produkts.

Benutzer können eine vorhandene Bestellung auswählen und die Bestelldetails abrufen. Wir kapseln diese Funktionalität in ein anderes Ansichtsmodell:

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);
            })
        });
    };
}

Die OrderDetailsViewModel Initialisierung erfolgt mit einer Bestellung und ruft die Bestelldetails ab, indem eine AJAX-Anforderung an den Server gesendet wird.

Beachten Sie außerdem die total Eigenschaft auf der OrderDetailsViewModel. Diese Eigenschaft ist eine spezielle Art von Observable, die als berechnete Observable bezeichnet wird. Wie der Name schon sagt, können Sie mit einer berechneten Observable-Funktion Daten an einen berechneten Wert binden – in diesem Fall die Gesamtkosten der Bestellung.

Fügen Sie als Nächstes die folgenden Funktionen hinzu:AppViewModel

  • resetCart entfernt alle Artikel aus dem Warenkorb.
  • getDetails ruft die Details für eine Bestellung ab (durch das Hinzufügen einer neuen OrderDetailsViewModel zur Liste details).
  • createOrder erstellt eine neue Bestellung und leert den Warenkorb.
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);
            }  
        });
    };
};

Initialisieren Sie schließlich das Ansichtsmodell, indem Sie AJAX-Anforderungen für die Produkte und Bestellungen vornehmen:

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, das ist viel Code, aber wir haben es schrittweise aufgebaut, also hoffentlich ist das Design klar. Jetzt können wir einige Knockout.js Bindungen zum HTML-Code hinzufügen.

Produkte

Hier sind die Bindungen für die Produktliste:

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

Das Produktarray wird durchlaufen, und der Name sowie der Preis werden angezeigt. Die Schaltfläche "Zu Bestellung hinzufügen" ist nur sichtbar, wenn der Benutzer angemeldet ist.

Die Schaltfläche "Zu Bestellung hinzufügen" ruft addItemToCart auf der ProductViewModel-Instanz für das Produkt auf. Dies veranschaulicht ein ansprechendes Feature von Knockout.js: Wenn ein Ansichtsmodell andere Ansichtsmodelle enthält, können Sie die Bindungen auf das innere Modell anwenden. In diesem Beispiel werden die Bindungen innerhalb des foreach auf jede der ProductViewModel-Instanzen angewendet. Dieser Ansatz ist viel sauberer als die gesamte Funktionalität in ein einzelnes Ansichtsmodell zu setzen.

Warenkorb

Hier sind die Bindungen für den Warenkorb:

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

Daher wird das Warenkorb-Array durchlaufen, und der Name, der Preis und die Menge werden angezeigt. Beachten Sie, dass der Link "Entfernen" und die Schaltfläche "Reihenfolge erstellen" an Ansichtsmodellfunktionen gebunden sind.

Bestellungen

Hier sind die Bindungen für die Auftragsliste:

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

Diese Schleife durchläuft die Bestellungen und die Auftrags-ID wird angezeigt. Das Click-Ereignis für den Link ist an die getDetails Funktion gebunden.

Bestelldetails

Hier sind die Bindungen für die Bestelldetails:

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

Die Artikel in der Bestellung werden durchlaufen, und das Produkt, der Preis und die Menge werden angezeigt. Das umgebende div-Element ist nur sichtbar, wenn das Detailarray mindestens ein Element enthält.

Fazit

In diesem Lernprogramm haben Sie eine Anwendung erstellt, die Entity Framework für die Kommunikation mit der Datenbank verwendet, und ASP.NET Web-API, um eine öffentlich zugängliche Schnittstelle über der Datenebene bereitzustellen. Wir verwenden ASP.NET MVC 4 zum Rendern der HTML-Seiten und Knockout.js plus jQuery, um dynamische Interaktionen ohne Seitenladevorgänge bereitzustellen.

Zusätzliche Ressourcen: