Auf Englisch lesen

Freigeben über


Teil 8: Einkaufswagen mit AJAX-Updates

von Jon Galloway

Der MVC Music Store ist eine Tutorialanwendung, die schrittweise erläutert, wie ASP.NET MVC und Visual Studio für die Webentwicklung verwendet werden.

Der MVC Music Store ist eine einfache Beispielspeicherimplementierung, die Musikalben online verkauft und grundlegende Websiteverwaltung, Benutzeranmeldung und Warenkorbfunktionen implementiert.

In dieser Tutorialreihe werden alle Schritte zum Erstellen der ASP.NET MVC Music Store-Beispielanwendung beschrieben. Teil 8 umfasst Warenkorb mit Ajax Updates.

Wir ermöglichen Es Benutzern, Alben ohne Registrierung in ihren Warenkorb zu legen, aber sie müssen sich als Gäste registrieren, um den Checkout abzuschließen. Der Einkaufs- und Checkout-Prozess wird in zwei Controller unterteilt: einen ShoppingCart-Controller, der das anonyme Hinzufügen von Elementen zu einem Einkaufswagen ermöglicht, und einen Checkout-Controller, der den Checkout-Prozess übernimmt. Wir beginnen mit dem Warenkorb in diesem Abschnitt und erstellen dann den Checkout-Prozess im folgenden Abschnitt.

Hinzufügen der Modellklassen Cart, Order und OrderDetail

In unseren Einkaufswagen- und Checkout-Prozessen werden einige neue Klassen verwendet. Klicken Sie mit der rechten Maustaste auf den Ordner Models, und fügen Sie eine Cart-Klasse (Cart.cs) mit dem folgenden Code hinzu.

using System.ComponentModel.DataAnnotations;
 
namespace MvcMusicStore.Models
{
    public class Cart
    {
        [Key]
        public int      RecordId    { get; set; }
        public string   CartId      { get; set; }
        public int      AlbumId     { get; set; }
        public int      Count       { get; set; }
        public System.DateTime DateCreated { get; set; }
        public virtual Album Album  { get; set; }
    }
}

Diese Klasse ähnelt den anderen, die wir bisher verwendet haben, mit Ausnahme des [Key]-Attributs für die RecordId-Eigenschaft. Unsere Warenkorbelemente verfügen über einen Zeichenfolgenbezeichner namens CartID, um anonymes Einkaufen zu ermöglichen, aber die Tabelle enthält einen ganzzahligen Primärschlüssel namens RecordId. Gemäß der Konvention erwartet Entity Framework Code-First, dass der Primärschlüssel für eine Tabelle mit dem Namen Cart entweder CartId oder ID ist, aber wir können dies problemlos über Anmerkungen oder Code überschreiben, wenn wir möchten. Dies ist ein Beispiel dafür, wie wir die einfachen Konventionen in Entity Framework Code-First verwenden können, wenn sie uns passen, aber wir sind nicht durch sie eingeschränkt, wenn sie nicht.

Fügen Sie als Nächstes eine Order-Klasse (Order.cs) mit dem folgenden Code hinzu.

using System.Collections.Generic;
 
namespace MvcMusicStore.Models
{
    public partial class Order
    {
        public int    OrderId    { get; set; }
        public string Username   { get; set; }
        public string FirstName  { get; set; }
        public string LastName   { get; set; }
        public string Address    { get; set; }
        public string City       { get; set; }
        public string State      { get; set; }
        public string PostalCode { get; set; }
        public string Country    { get; set; }
        public string Phone      { get; set; }
        public string Email      { get; set; }
        public decimal Total     { get; set; }
        public System.DateTime OrderDate      { get; set; }
        public List<OrderDetail> OrderDetails { get; set; }
    }
}

Diese Klasse verfolgt Zusammenfassungs- und Lieferinformationen für eine Bestellung nach. Es wird noch nicht kompiliert, da es über eine OrderDetails-Navigationseigenschaft verfügt, die von einer Klasse abhängt, die wir noch nicht erstellt haben. Beheben Sie dies jetzt, indem Sie eine Klasse namens OrderDetail.cs hinzufügen und den folgenden Code hinzufügen.

namespace MvcMusicStore.Models
{
    public class OrderDetail
    {
        public int OrderDetailId { get; set; }
        public int OrderId { get; set; }
        public int AlbumId { get; set; }
        public int Quantity { get; set; }
        public decimal UnitPrice { get; set; }
        public virtual Album Album { get; set; }
        public virtual Order Order { get; set; }
    }
}

Wir nehmen ein letztes Update an unserer MusicStoreEntities-Klasse vor, um DbSets einzuschließen, die diese neuen Model-Klassen verfügbar machen, einschließlich eines DbSet-Künstlers<>. Die aktualisierte MusicStoreEntities-Klasse wird wie folgt angezeigt.

using System.Data.Entity;
 
namespace MvcMusicStore.Models
{
    public class MusicStoreEntities : DbContext
    {
        public DbSet<Album>     Albums  { get; set; }
        public DbSet<Genre>     Genres  { get; set; }
        public DbSet<Artist>    Artists {
get; set; }
        public DbSet<Cart>     
Carts { get; set; }
        public DbSet<Order>     Orders
{ get; set; }
        public DbSet<OrderDetail>
OrderDetails { get; set; }
    }
}

Verwalten der Einkaufswagen-Geschäftslogik

Als Nächstes erstellen wir die ShoppingCart-Klasse im Ordner Models. Das ShoppingCart-Modell verarbeitet den Datenzugriff auf die Warenkorbtabelle. Darüber hinaus wird die Geschäftslogik für das Hinzufügen und Entfernen von Elementen aus dem Warenkorb verarbeitet.

Da wir nicht verlangen möchten, dass sich Benutzer für ein Konto registrieren, nur um Artikel zu ihrem Warenkorb hinzuzufügen, weisen wir Benutzern beim Zugriff auf den Warenkorb einen temporären eindeutigen Bezeichner (mithilfe einer GUID oder global eindeutigen Bezeichners) zu. Diese ID wird mithilfe der ASP.NET Session-Klasse gespeichert.

Hinweis: Die ASP.NET-Sitzung ist ein bequemer Ort, um benutzerspezifische Informationen zu speichern, die nach dem Verlassen der Website ablaufen. Während der Missbrauch des Sitzungszustands Auswirkungen auf die Leistung auf größeren Websites haben kann, funktioniert unsere Leichte Verwendung gut für Demonstrationszwecke.

Die ShoppingCart-Klasse macht die folgenden Methoden verfügbar:

AddToCart nimmt ein Album als Parameter an und fügt es dem Warenkorb des Benutzers hinzu. Da die Cart-Tabelle die Menge für jedes Album nachverfolgt, enthält sie logik, bei Bedarf eine neue Zeile zu erstellen oder die Menge einfach zu erhöhen, wenn der Benutzer bereits eine Kopie des Albums bestellt hat.

RemoveFromCart nimmt eine Album-ID und entfernt sie aus dem Warenkorb des Benutzers. Wenn der Benutzer nur eine Kopie des Albums im Warenkorb hatte, wird die Zeile entfernt.

EmptyCart entfernt alle Elemente aus dem Warenkorb eines Benutzers.

GetCartItems ruft eine Liste von CartItems zur Anzeige oder Verarbeitung ab.

GetCount ruft die Gesamtzahl der Alben ab, die ein Benutzer im Warenkorb hat.

GetTotal berechnet die Gesamtkosten aller Artikel im Warenkorb.

CreateOrder konvertiert den Warenkorb während der Bestellphase in eine Bestellung.

GetCart ist eine statische Methode, die es unseren Controllern ermöglicht, ein Cart-Objekt abzurufen. Es verwendet die GetCartId-Methode , um das Lesen der CartId aus der Sitzung des Benutzers zu verarbeiten. Die GetCartId-Methode erfordert httpContextBase, damit sie die CartId des Benutzers aus der Sitzung des Benutzers lesen kann.

Hier ist die vollständige ShoppingCart-Klasse:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MvcMusicStore.Models
{
    public partial class ShoppingCart
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();
        string ShoppingCartId { get; set; }
        public const string CartSessionKey = "CartId";
        public static ShoppingCart GetCart(HttpContextBase context)
        {
            var cart = new ShoppingCart();
            cart.ShoppingCartId = cart.GetCartId(context);
            return cart;
        }
        // Helper method to simplify shopping cart calls
        public static ShoppingCart GetCart(Controller controller)
        {
            return GetCart(controller.HttpContext);
        }
        public void AddToCart(Album album)
        {
            // Get the matching cart and album instances
            var cartItem = storeDB.Carts.SingleOrDefault(
                c => c.CartId == ShoppingCartId 
                && c.AlbumId == album.AlbumId);
 
            if (cartItem == null)
            {
                // Create a new cart item if no cart item exists
                cartItem = new Cart
                {
                    AlbumId = album.AlbumId,
                    CartId = ShoppingCartId,
                    Count = 1,
                    DateCreated = DateTime.Now
                };
                storeDB.Carts.Add(cartItem);
            }
            else
            {
                // If the item does exist in the cart, 
                // then add one to the quantity
                cartItem.Count++;
            }
            // Save changes
            storeDB.SaveChanges();
        }
        public int RemoveFromCart(int id)
        {
            // Get the cart
            var cartItem = storeDB.Carts.Single(
                cart => cart.CartId == ShoppingCartId 
                && cart.RecordId == id);
 
            int itemCount = 0;
 
            if (cartItem != null)
            {
                if (cartItem.Count > 1)
                {
                    cartItem.Count--;
                    itemCount = cartItem.Count;
                }
                else
                {
                    storeDB.Carts.Remove(cartItem);
                }
                // Save changes
                storeDB.SaveChanges();
            }
            return itemCount;
        }
        public void EmptyCart()
        {
            var cartItems = storeDB.Carts.Where(
                cart => cart.CartId == ShoppingCartId);
 
            foreach (var cartItem in cartItems)
            {
                storeDB.Carts.Remove(cartItem);
            }
            // Save changes
            storeDB.SaveChanges();
        }
        public List<Cart> GetCartItems()
        {
            return storeDB.Carts.Where(
                cart => cart.CartId == ShoppingCartId).ToList();
        }
        public int GetCount()
        {
            // Get the count of each item in the cart and sum them up
            int? count = (from cartItems in storeDB.Carts
                          where cartItems.CartId == ShoppingCartId
                          select (int?)cartItems.Count).Sum();
            // Return 0 if all entries are null
            return count ?? 0;
        }
        public decimal GetTotal()
        {
            // Multiply album price by count of that album to get 
            // the current price for each of those albums in the cart
            // sum all album price totals to get the cart total
            decimal? total = (from cartItems in storeDB.Carts
                              where cartItems.CartId == ShoppingCartId
                              select (int?)cartItems.Count *
                              cartItems.Album.Price).Sum();

            return total ?? decimal.Zero;
        }
        public int CreateOrder(Order order)
        {
            decimal orderTotal = 0;
 
            var cartItems = GetCartItems();
            // Iterate over the items in the cart, 
            // adding the order details for each
            foreach (var item in cartItems)
            {
                var orderDetail = new OrderDetail
                {
                    AlbumId = item.AlbumId,
                    OrderId = order.OrderId,
                    UnitPrice = item.Album.Price,
                    Quantity = item.Count
                };
                // Set the order total of the shopping cart
                orderTotal += (item.Count * item.Album.Price);
 
                storeDB.OrderDetails.Add(orderDetail);
 
            }
            // Set the order's total to the orderTotal count
            order.Total = orderTotal;
 
            // Save the order
            storeDB.SaveChanges();
            // Empty the shopping cart
            EmptyCart();
            // Return the OrderId as the confirmation number
            return order.OrderId;
        }
        // We're using HttpContextBase to allow access to cookies.
        public string GetCartId(HttpContextBase context)
        {
            if (context.Session[CartSessionKey] == null)
            {
                if (!string.IsNullOrWhiteSpace(context.User.Identity.Name))
                {
                    context.Session[CartSessionKey] =
                        context.User.Identity.Name;
                }
                else
                {
                    // Generate a new random GUID using System.Guid class
                    Guid tempCartId = Guid.NewGuid();
                    // Send tempCartId back to client as a cookie
                    context.Session[CartSessionKey] = tempCartId.ToString();
                }
            }
            return context.Session[CartSessionKey].ToString();
        }
        // When a user has logged in, migrate their shopping cart to
        // be associated with their username
        public void MigrateCart(string userName)
        {
            var shoppingCart = storeDB.Carts.Where(
                c => c.CartId == ShoppingCartId);
 
            foreach (Cart item in shoppingCart)
            {
                item.CartId = userName;
            }
            storeDB.SaveChanges();
        }
    }
}

ViewModels

Unser Warenkorbcontroller muss einige komplexe Informationen an seine Ansichten übermitteln, die unseren Modellobjekten nicht sauber zugeordnet werden. Wir möchten unsere Modelle nicht an unsere Ansichten anpassen. Modellklassen sollten unsere Domäne und nicht die Benutzeroberfläche darstellen. Eine Lösung wäre, die Informationen mithilfe der ViewBag-Klasse an unsere Ansichten zu übergeben, wie wir es mit der Dropdown-Liste Store Manager getan haben, aber das Übergeben vieler Informationen über ViewBag ist schwierig zu verwalten.

Eine Lösung hierfür ist die Verwendung des ViewModel-Musters . Bei Verwendung dieses Musters erstellen wir stark typisierte Klassen, die für unsere spezifischen Ansichtsszenarien optimiert sind und Eigenschaften für die dynamischen Werte/Inhalte verfügbar machen, die von unseren Ansichtsvorlagen benötigt werden. Unsere Controllerklassen können diese ansichtsoptimierten Klassen dann auffüllen und an unsere Ansichtsvorlage übergeben, um sie zu verwenden. Dies ermöglicht Typsicherheit, Kompilierzeitüberprüfung und IntelliSense-Editor innerhalb von Ansichtsvorlagen.

Wir erstellen zwei Ansichtsmodelle für die Verwendung in unserem Warenkorb-Controller: Das ShoppingCartViewModel enthält den Inhalt des Einkaufswagens des Benutzers, und das ShoppingCartRemoveViewModel wird verwendet, um Bestätigungsinformationen anzuzeigen, wenn ein Benutzer etwas aus dem Warenkorb entfernt.

Im Stamm unseres Projekts erstellen wir einen neuen ViewModels-Ordner, um die Organisation zu erhalten. Klicken Sie mit der rechten Maustaste auf das Projekt, und wählen Sie Hinzufügen/Neuer Ordner aus.

Screenshot des Projektfensters mit dem Rechtsklickmenü mit gelb hervorgehobenen Optionen

Nennen Sie den Ordner ViewModels.

Screenshot des Projektmappen-Explorer, der den neu erstellten und neu benannten Ordner

Fügen Sie als Nächstes die ShoppingCartViewModel-Klasse im Ordner ViewModels hinzu. Es verfügt über zwei Eigenschaften: eine Liste der Warenkorbelemente und einen Dezimalwert, der den Gesamtpreis für alle Artikel im Warenkorb enthält.

using System.Collections.Generic;
using MvcMusicStore.Models;
 
namespace MvcMusicStore.ViewModels
{
    public class ShoppingCartViewModel
    {
        public List<Cart> CartItems { get; set; }
        public decimal CartTotal { get; set; }
    }
}

Fügen Sie nun shoppingCartRemoveViewModel dem Ordner ViewModels mit den folgenden vier Eigenschaften hinzu.

namespace MvcMusicStore.ViewModels
{
    public class ShoppingCartRemoveViewModel
    {
        public string Message { get; set; }
        public decimal CartTotal { get; set; }
        public int CartCount { get; set; }
        public int ItemCount { get; set; }
        public int DeleteId { get; set; }
    }
}

Der Warenkorbcontroller

Der Warenkorbcontroller hat drei Standard Zwecke: Hinzufügen von Artikeln zu einem Warenkorb, Entfernen von Artikeln aus dem Warenkorb und Anzeigen von Artikeln im Warenkorb. Dabei werden die drei soeben erstellten Klassen verwendet: ShoppingCartViewModel, ShoppingCartRemoveViewModel und ShoppingCart. Wie in StoreController und StoreManagerController fügen wir ein Feld hinzu, um eine instance von MusicStoreEntities zu speichern.

Fügen Sie dem Projekt mithilfe der Vorlage Leerer Controller einen neuen Einkaufswagencontroller hinzu.

Screenshot des Fensters

Hier ist der vollständige ShoppingCart Controller. Die Aktionen Index und Controller hinzufügen sollten sehr vertraut aussehen. Die Aktionen Entfernen und CartSummary-Controller behandeln zwei Sonderfälle, die im folgenden Abschnitt erläutert werden.

using System.Linq;
using System.Web.Mvc;
using MvcMusicStore.Models;
using MvcMusicStore.ViewModels;
 
namespace MvcMusicStore.Controllers
{
    public class ShoppingCartController : Controller
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();
        //
        // GET: /ShoppingCart/
        public ActionResult Index()
        {
            var cart = ShoppingCart.GetCart(this.HttpContext);
 
            // Set up our ViewModel
            var viewModel = new ShoppingCartViewModel
            {
                CartItems = cart.GetCartItems(),
                CartTotal = cart.GetTotal()
            };
            // Return the view
            return View(viewModel);
        }
        //
        // GET: /Store/AddToCart/5
        public ActionResult AddToCart(int id)
        {
            // Retrieve the album from the database
            var addedAlbum = storeDB.Albums
                .Single(album => album.AlbumId == id);
 
            // Add it to the shopping cart
            var cart = ShoppingCart.GetCart(this.HttpContext);
 
            cart.AddToCart(addedAlbum);
 
            // Go back to the main store page for more shopping
            return RedirectToAction("Index");
        }
        //
        // AJAX: /ShoppingCart/RemoveFromCart/5
        [HttpPost]
        public ActionResult RemoveFromCart(int id)
        {
            // Remove the item from the cart
            var cart = ShoppingCart.GetCart(this.HttpContext);
 
            // Get the name of the album to display confirmation
            string albumName = storeDB.Carts
                .Single(item => item.RecordId == id).Album.Title;
 
            // Remove from cart
            int itemCount = cart.RemoveFromCart(id);
 
            // Display the confirmation message
            var results = new ShoppingCartRemoveViewModel
            {
                Message = Server.HtmlEncode(albumName) +
                    " has been removed from your shopping cart.",
                CartTotal = cart.GetTotal(),
                CartCount = cart.GetCount(),
                ItemCount = itemCount,
                DeleteId = id
            };
            return Json(results);
        }
        //
        // GET: /ShoppingCart/CartSummary
        [ChildActionOnly]
        public ActionResult CartSummary()
        {
            var cart = ShoppingCart.GetCart(this.HttpContext);
 
            ViewData["CartCount"] = cart.GetCount();
            return PartialView("CartSummary");
        }
    }
}

Ajax Updates mit jQuery

Als Nächstes erstellen wir eine Einkaufswagenindexseite, die stark in shoppingCartViewModel eingegeben ist und die Listenansichtsvorlage mit der gleichen Methode wie zuvor verwendet.

Screenshot des Fensters Ansicht hinzufügen mit dem Feld Ansichtsname, den Dropdownlisten Ansichtsmodul, Modellklasse und Gerüstbau sowie der Auswahl

Anstatt jedoch einen Html.ActionLink zu verwenden, um Elemente aus dem Warenkorb zu entfernen, verwenden wir jQuery, um das Klickereignis für alle Links in dieser Ansicht mit der HTML-Klasse RemoveLink zu "verknüpfen". Anstatt das Formular zu veröffentlichen, führt dieser Klickereignishandler einfach einen AJAX-Rückruf für unsere RemoveFromCart-Controlleraktion aus. RemoveFromCart gibt ein JSON-serialisiertes Ergebnis zurück, das unser jQuery-Rückruf dann analysiert und vier schnelle Aktualisierungen der Seite mithilfe von jQuery durchführt:

    1. Entfernt das gelöschte Album aus der Liste.
    1. Updates der Anzahl des Warenkorbs im Header
    1. Zeigt dem Benutzer eine Updatemeldung an.
    1. Updates Des Warenkorbs Gesamtpreis

Da das Remove-Szenario von einem Ajax-Rückruf in der Indexansicht behandelt wird, benötigen wir keine zusätzliche Ansicht für die RemoveFromCart-Aktion. Hier sehen Sie den vollständigen Code für die Ansicht /ShoppingCart/Index:

@model MvcMusicStore.ViewModels.ShoppingCartViewModel
@{
    ViewBag.Title = "Shopping Cart";
}
<script src="/Scripts/jquery-1.4.4.min.js"
type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        // Document.ready -> link up remove event handler
        $(".RemoveLink").click(function () {
            // Get the id from the link
            var recordToDelete = $(this).attr("data-id");
            if (recordToDelete != '') {
                // Perform the ajax post
                $.post("/ShoppingCart/RemoveFromCart", {"id": recordToDelete },
                    function (data) {
                        // Successful requests get here
                        // Update the page elements
                        if (data.ItemCount == 0) {
                            $('#row-' + data.DeleteId).fadeOut('slow');
                        } else {
                            $('#item-count-' + data.DeleteId).text(data.ItemCount);
                        }
                        $('#cart-total').text(data.CartTotal);
                        $('#update-message').text(data.Message);
                        $('#cart-status').text('Cart (' + data.CartCount + ')');
                    });
            }
        });
    });
</script>
<h3>
    <em>Review</em> your cart:
 </h3>
<p class="button">
    @Html.ActionLink("Checkout
>>", "AddressAndPayment", "Checkout")
</p>
<div id="update-message">
</div>
<table>
    <tr>
        <th>
            Album Name
        </th>
        <th>
            Price (each)
        </th>
        <th>
            Quantity
        </th>
        <th></th>
    </tr>
    @foreach (var item in
Model.CartItems)
    {
        <tr id="row-@item.RecordId">
            <td>
                @Html.ActionLink(item.Album.Title,
"Details", "Store", new { id = item.AlbumId }, null)
            </td>
            <td>
                @item.Album.Price
            </td>
            <td id="item-count-@item.RecordId">
                @item.Count
            </td>
            <td>
                <a href="#" class="RemoveLink"
data-id="@item.RecordId">Remove
from cart</a>
            </td>
        </tr>
    }
    <tr>
        <td>
            Total
        </td>
        <td>
        </td>
        <td>
        </td>
        <td id="cart-total">
            @Model.CartTotal
        </td>
    </tr>
</table>

Um dies zu testen, müssen wir in der Lage sein, Artikel zu unserem Warenkorb hinzuzufügen. Wir aktualisieren unsere Ansicht "Storedetails ", um die Schaltfläche "In den Warenkorb" aufzunehmen. Während wir dabei sind, können wir einige der zusätzlichen Albuminformationen hinzufügen, die wir seit der letzten Aktualisierung dieser Ansicht hinzugefügt haben: Genre, Künstler, Preis und Albumart. Der aktualisierte Code der Storedetailsansicht wird wie unten dargestellt angezeigt.

@model MvcMusicStore.Models.Album
@{
    ViewBag.Title = "Album - " + Model.Title;
 }
<h2>@Model.Title</h2>
<p>
    <img alt="@Model.Title"
src="@Model.AlbumArtUrl" />
</p>
<div id="album-details">
    <p>
        <em>Genre:</em>
        @Model.Genre.Name
    </p>
    <p>
        <em>Artist:</em>
        @Model.Artist.Name
    </p>
    <p>
        <em>Price:</em>
        @String.Format("{0:F}",
Model.Price)
    </p>
    <p class="button">
        @Html.ActionLink("Add to
cart", "AddToCart", 
        "ShoppingCart", new { id = Model.AlbumId }, "")
    </p>
</div>

Jetzt können wir uns durch den Store klicken und das Hinzufügen und Entfernen von Alben zu und aus unserem Warenkorb testen. Führen Sie die Anwendung aus, und navigieren Sie zum Store Index.

Screenshot des Fensters

Klicken Sie als Nächstes auf ein Genre, um eine Liste der Alben anzuzeigen.

Screenshot des Fensters

Wenn Sie auf einen Albumtitel klicken, wird jetzt unsere aktualisierte Albumdetailseite angezeigt, einschließlich der Schaltfläche "In den Warenkorb".

Screenshot des Fensters

Wenn Sie auf die Schaltfläche "In den Warenkorb hinzufügen" klicken, wird unsere Warenkorbindexansicht mit der Warenkorbzusammenfassungsliste angezeigt.

Screenshot des Fensters

Nachdem Sie Ihren Warenkorb geladen haben, können Sie auf den Link Aus Warenkorb entfernen klicken, um das Ajax-Update in Ihren Warenkorb zu sehen.

Screenshot des Fensters

Wir haben einen funktionierenden Warenkorb erstellt, der es nicht registrierten Benutzern ermöglicht, Ihrem Warenkorb Artikel hinzuzufügen. Im folgenden Abschnitt können sie sich registrieren und den Bestellvorgang abschließen.