Confinity Documentation
  • Latest Version
  • Latest Version
  • Getting Started

    • Introduction
    • Core Concepts
    • Create an Application
    • Glossary
  • Essentials

    • Authentication & SSO
    • Breaking Changes
    • Roslyn Source Analyzers
    • Changelog
    • ConfinityContent
    • ConfinitySelectable
    • Confinity Schedules
    • Data Seeding
    • Development guidelines [WIP]
    • Entity App
    • Entity Form
    • Entity Permissions
    • Frontend Configuration
    • Images
    • Known Issues
    • Localization
    • Migrations
    • Modules [WIP]
    • On-Site Editing
    • Settings
    • Cascade Delete
    • Replication
    • Infrastructure
  • Modules

    • Analytics Module
    • Assets Module
    • Blog Module
    • Cookie Consent Module
    • Forms Module
    • Friendly Captcha (Forms Module )
    • GeoIP Module
    • Htmx
    • Mail Module
    • Mailing Module
    • MediaPlayer Module
    • GoogleMyBusiness Module
    • OpenTelemetry Module
    • Pages Module [WIP]
    • Pattern Library Module
    • SIX Saferpay (worldline) Module
    • Products Module
    • Search Module
    • Wizard Module
  • Guides

    • Create a Custom Entity App Form Element
    • Date and Time
    • Entity Change Listener
    • File Upload / Temp File
    • HTTP security headers
    • conventions [WIP]
    • How to use Confinity with nginx
    • Notifications
    • Nullability
    • Rename Entity
    • Schedules
    • Useful snippets
    • Content Localization
  • Design Guidelines

    • Introduction
    • Page Components
    • Forms Module

Products Module

With the products module, an author can manage product information in a structured and centralized way.

Display Product Information in a Page Component

In the following example, you will see how you can create a page component which will display some product attributes in a table, configured by the author.

First of all, create a page component model, which allows the author to select a product and product attributes to display.


public class ProductAttributeListViewModel
{
    public ProductModel? Product { get; set; }
    public IEnumerable<Guid> AttributeIds { get; set; } = [];
}

Then register the page component and build the form for the author.


module.Configure.RegisterConfinityContent<MyProductTableComponentModel>("My Product Table")
    .Icon(Icon.Table)
    .Editor(form =>
    {
        form.AddTab(tab =>
        {
            tab.AddSelectProduct(p => p.Product!)
                .Validate(v => v.NotNull());
            tab.AddSelectProductAttributes(p => p.Attributes!)
                .Validate(v => v.NotNull());
        });
    });

Now create a ViewComponent which loads the product from the product service and prepare the data for the view.


public class MyProductTableComponent : ViewComponent
{
    private readonly IProductService _productService;

    public MyProductTableComponent(IProductService productService)
    {
        _productService = productService;
    }

    public async Task<IViewComponentResult> InvokeAsync(MyProductTableComponentModel model)
    {
        var viewModel = new ProductAttributeListViewModel()
        {
            Product = await _productService.GetProductAsync(model.Product?.Id),
            AttributeIds = model.Attributes?.Select(p => p.Id) ?? []
        };

        return View("./MyProductTableComponent.cshtml", viewModel);
    }
}

And finally we create the view for the component which will get all attributes from the product grouped by its attribute groups. Then we loop all attributes and display the values accordingly. In the following example you will find all kind of value types a product attribute can have.

@using Confinity.Assets
@using Confinity.Entities
@using Confinity.Models
@using Confinity.WebUI.Util
@using Microsoft.AspNetCore.Html
@model DocsDemos.Modules.Products.ProductAttributeListViewModel
@inject IAssetService AssetService;

@if (Model.Product != null)
{
    foreach (var group in Model.Product
                 .GetGroupedAttributes()
                 .Where(p => p.Attributes.Any(p => p.Value != null && Model.AttributeIds.Contains(p.Id)
                 ))
            )
    {
        <h2>@group.Name</h2>
        <table class="table table-bordered">
            @foreach (var attr in group.Attributes
                              .Where(p => p.Value != null && Model.AttributeIds.Contains(p.Id))
                         )
            {
                <tr>
                    <th>@attr.Name</th>
                    <td>
                        @switch (attr.Value)
                        {
                            case string:
                            case int:
                            case double:
                                @attr.Value
                                break;
                            case HtmlString html:
                                @await Html.FromWysiwygAsync(html)
                                break;
                            case bool isTrue:
                                @(isTrue ? "Yes" : "No")
                                break;
                            case IEnumerable<string> list:
                                <ul>
                                    @foreach (var item in list)
                                    {
                                        <li>@item</li>
                                    }
                                </ul>
                                break;
                            case EntityRef<Asset> asset:
                                @await Component.InvokeAsync(typeof(ConfinityPicture),
                                    new ConfinityPictureModel(await AssetService.GetImageAsset(asset)))
                                break;
                            case IEnumerable<EntityRef<Asset>> assets:
                                foreach (var asset in assets)
                                {
                                    @await Component.InvokeAsync(typeof(ConfinityPicture),
                                        new ConfinityPictureModel(await AssetService.GetImageAsset(asset)))
                                }

                                break;
                            case LinkModel link:
                                <a confinity-link="link"></a>
                                break;
                            case IEnumerable<LinkModel> links:
                                <ul>
                                    @foreach (var link in links)
                                    {
                                        <li>
                                            <a confinity-link="link"></a>
                                        </li>
                                    }
                                </ul>
                                break;
                            default:
                                <text>unknown type (@attr.Value?.GetType())</text>
                                break;
                        }
                        @attr.Unit
                    </td>
                </tr>
            }
        </table>
    }
}
else
{
    <p>Product not found</p>
}

Attribute value types

The following return types are possible for a AttributeValueModel.Value:

  • bool
  • double
  • int
  • string
  • HtmlString
  • LinkModel
  • EntityRef<Asset>
  • EntityRef<T> (where T is one of your registered entities)
  • IEnumerable<double>
  • IEnumerable<int>
  • IEnumerable<string>
  • IEnumerable<LinkModel>
  • IEnumerable<EntityRef<Asset>>
  • IEnumerable<EntityRef<T> (where T is one of your registered entities)>

Reference Entities From a Product

You might want to reference your entities from a product. For this case, we have built the product attribute type "Reference (single)" and "References (multiple)", which allows the author to select an entity, which then can be referenced by a product. But first, you have to register the entity in order an author can select it.

To do so, you have to register the entity via .Net's Options API and add your configuration to the list EntityReferenceTypes in ConfinityProductsOptions like in the following example.


services.Configure<ConfinityProductsOptions>(options =>
{
    options.EntityReferenceTypes.Add(
        new EntityReferenceOption<Page>
        {
            LabelExpression = entity => entity.Name + " " + entity.Slug,
            TypeLabel = "Articles"
        });
});

Prev
SIX Saferpay (worldline) Module
Next
Search Module