Kentico Xperience 13 documentation and ASP.NET Core

Most documentation about running Xperience applications under ASP.NET Core can be found in a dedicated section: Developing Xperience applications using ASP.NET Core. The rest of the documentation still applies, but some code samples and scenarios might need slight modifications for Core projects.

All major differences between the MVC 5 and Core platforms are summarized in Migrating to ASP.NET Core.

×

Displaying product details

When building e-commerce sites, you often need to display product catalog and product details pages. Product details pages usually include more comprehensive information about specific products.

This page describes how to display products from the Products application in a live site application connected to Xperience. 

Note that the examples were created for a site that uses content tree-based routing.

Displaying product details pages

Tip: View the full code of a functional example in the LearningKit project on GitHub. You can also run the LearningKit website by connecting the project to a Xperience database. Follow the instructions in the repository's README file.

On this page

  1. Open your live site project in Visual Studio.
  2. Add a generic view model representing products. Only include the properties that you wish to display or otherwise require. Create separate view models if you require properties specific to a given product.

            public readonly PriceDetailViewModel PriceDetail;
            public readonly string Name;
            public readonly string Description;
            public readonly string ShortDescription;
            public readonly int SKUID;
            public readonly string ImagePath;
            public readonly bool IsInStock;
            
            /// <summary>
            /// Creates a new product model.
            /// </summary>
            /// <param name="productPage">Product's page.</param>
            /// <param name="priceDetail">Price of the product.</param>
            public ProductViewModel(SKUTreeNode productPage, ProductCatalogPrices priceDetail)
            {
                // Fills the page information   
                Name = productPage.DocumentName;
                Description = productPage.DocumentSKUDescription;
                ShortDescription = productPage.DocumentSKUShortDescription;
                
                // Fills the SKU information
                SKUInfo sku = productPage.SKU;
                SKUID = sku.SKUID;
                ImagePath = string.IsNullOrEmpty(sku.SKUImagePath) ? null : new FileUrl(sku.SKUImagePath, true)
                                                                                .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(400))
                                                                                .RelativePath;
     
                IsInStock = sku.SKUTrackInventory == TrackInventoryTypeEnum.Disabled ||
                            sku.SKUAvailableItems > 0;
     
                PriceDetail = new PriceDetailViewModel()
                {
                    Price = priceDetail.Price,
                    ListPrice = priceDetail.ListPrice,
                    CurrencyFormatString = priceDetail.Currency.CurrencyFormatString
                };
            }
    

  3. Add a new controller with an action that displays the product details page. The example also retrieves the coupled product page.

    Initializing services
            /// <summary>
            /// Constructor for the ProductController class.
            /// </summary>
            public ProductController(IShoppingService shoppingService,
                                     ICatalogPriceCalculatorFactory priceCalculatorFactory,
                                     IPageDataContextRetriever pageRetriever,
                                     ISiteService siteService,
                                     ISKUInfoProvider skuInfoProvider)
            {
                // Initializes instances of services required to manage product price calculation and the shopping cart
                this.shoppingService = shoppingService;
                this.priceCalculatorFactory = priceCalculatorFactory;
                this.pageRetriever = pageRetriever;
                this.siteService = siteService;
                this.skuInfoProvider = skuInfoProvider;
            }
    

    We recommend using a dependency injection container to initialize instances of used API services (e.g. IShoppingService ).

            /// <summary>
            /// Displays a product detail page of a product.
            /// </summary>
            public ActionResult Detail()
            {
                // Gets the product from the data context
                SKUTreeNode product = GetProduct();
     
                // If the product is not found or if it is not allowed for sale, redirects to error 404
                if ((product == null) || (product.SKU == null) || !product.SKU.SKUEnabled)
                {
                    return HttpNotFound();
                }
     
                // Initializes the view model of the product with a calculated price
                ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();
     
                ProductCatalogPrices price = catalogPriceCalculatorFactory
                    .GetCalculator(cart.ShoppingCartSiteID)
                    .GetPrices(product.SKU, Enumerable.Empty<SKUInfo>(), cart);
     
                // Fills the product model with retrieved data
                ProductViewModel viewModel = new ProductViewModel(product, price);
     
                // Displays the product details page
                return View("Detail", viewModel);
            }
     
     
            /// <summary>
            /// Retrieves the product.
            /// </summary>
            private SKUTreeNode GetProduct()
            {
                // Gets the current page from the router data context
                TreeNode node = pageRetriever.Retrieve<TreeNode>().Page;
     
                // If the found page is not a product, returns null
                if (node == null || !node.IsProduct())
                {
                    return null;
                }
     
                // Loads specific fields of the product's product page type from the database
                node.MakeComplete(true);
     
                // Returns the found page as a product page
                return node as SKUTreeNode;
            }
    

  4. Add a view that sets the appearance of the product details page.

    @model LearningKit.Models.Products.ProductViewModel
     
    <h2>@Model.Name</h2>
     
    @* Renders product details. *@
    @if (!string.IsNullOrEmpty(Model.ImagePath))
    {
        <img src="@Model.ImagePath" alt="@Model.Name">
    }
    <ul>
        <li>
            Description: @Html.Raw(@Model.Description)
        </li>
        <li>
            In stock:
            @if (!Model.IsInStock)
            {
                <span id="stockMessage">Yes</span>
            }
            else
            {
                <span id="stockMessage">No</span>
            }
        </li>
        <li>
            Total price: <span id="totalPrice">@String.Format(Model.PriceDetail.CurrencyFormatString, Model.PriceDetail.Price)</span>
        </li>
    </ul>
    

    Include a button that adds the product to a shopping cart. The button in the example calls the AddItem POST action from the CheckoutController implemented in Integrating the shopping cart as part of the Implementing a checkout process series.
        @* Renders an add to cart button. *@
        using (Html.BeginForm("AddItem", "Checkout", FormMethod.Post))
        {
            <input type="hidden" name="itemSkuId" value="@Model.SKUID" />
            <label>Qty</label>
            <input type="text" name="itemUnits" value="1" />
            <input type="submit" name="AddItem" value="Add to cart" />
        }
    

Customers can now browse product details pages. They are also able to add a product to their shopping cart directly from a product's detail page.

You can access the product page type's fields through generated model classes. See more in Generating classes for Xperience objects.

Getting URLs of product pages from SKU data

When generating links to product details pages from pages that only hold a product's SKU information, you first need to retrieve the coupled product page and use it to build a qualified URL:

  1. Edit the controller class you want to modify.
  2. Add an action that retrieves a product page from a given SKU identifier.

            /// <summary>
            /// Redirects to a product detail page based on the ID of a product's SKU object.
            /// </summary>
            /// <param name="skuID">ID of the product's SKU object.</param>
            public ActionResult ItemDetail(int skuID)
            {
                // Gets the SKU object
                SKUInfo sku = skuInfo.Get(skuID);
     
                // If the SKU does not exist or it is a product option, returns error 404
                if (sku == null || sku.IsProductOption)
                {
                    return HttpNotFound();
                }
     
                // If the SKU is a product variant, uses its parent product's ID
                if (sku.IsProductVariant)
                {
                    skuID = sku.SKUParentSKUID;
                }
     
                // Gets the product's page
                TreeNode node = pageRetriever.Retrieve<TreeNode>(query => query
                                    .Culture("en-us")
                                    .CombineWithDefaultCulture()
                                    .WhereEquals("NodeSKUID", skuID))
                                    .FirstOrDefault();
     
                // Returns 404 if no page for the specified product exists
                if (node == null)
                {
                    return HttpNotFound();
                }
     
                // Gets the product page's URL 
                string pageUrl = pageUrlRetriever.Retrieve(node).AbsoluteUrl;
     
                return Redirect(pageUrl);
            }
    

    If you do not use product variants in your store, you can move the variant check to the condition that returns a Not Found response, or remove it completely.

  3. Add a link to the ItemDetail action method to views where you only have access to the SKU identifier value.

    @Html.ActionLink(model.SKUName, "ItemDetail", new { skuId = model.SKUID })
    
    OR
    <a href="@Url.Action("ItemDetail", new { skuId = model.SKUID })">link text</a>
    

You are now able to build valid URLs to product details pages even when you do not have the product's page information available.


Was this page helpful?