Hide menu Last updated: Mar 16 2017
Area: Episerver Commerce Applies to versions: Commerce 10.4 and higher
Other versions:

Customizing facets in Campaign view

Facets in Episerver Commerce allow users to apply filters to campaigns and discounts, for easier location of specific items in the Campaign view. This topic describes how to customize the facets.

How it works

Facet Group

FacetGroup contains properties that identify a group of items by which to filter a campaign and settings to configure a group. Initialize a FacetGroup through a constructor. For example:

public FacetGroup(string id, string name, IEnumerable<FacetItem> items, FacetGroupSettings settings)
{
    Id = id; // the id of facet group
    Name = name; // the name of facet group
    Items = new List<FacetItem>(items); // the list facet item 
    Settings = settings; // settings to configuration a facet group
}

Facet Group Settings

FacetGroupSettings contains properties to configure a facet group. Initialize these properties through a constructor. For example:

public FacetGroupSettings(
    FacetSelectionType selectionType,
    int itemsToShow,
    bool collapsible,
    bool hasIcons,
    bool showMatchingItems,
    IEnumerable<string> dependsOn)
{
    SelectionType = selectionType; // Determine the selection type of facet group, single or multiple through FacetSelectionType enum
    ItemsToShow = itemsToShow; // The number to determine how many facet items will be show as default. If there are more than facet items, show more option will be available
    Collapsible = collapsible; // Determine the facet group is collapsible or not
    HasIcons = hasIcons; // Determine the icon of facet items are displayed or not
    ShowMatchingItems = showMatchingItems; // Determine the number of matching filtered items are shown or not
    DependsOn = dependsOn; // List of facet group that current group depends on. That mean changes the list facet group could affect the number of matching filterd items in the current group
}

Facet Item

FacetItem contains properties that identify a facet item in the facet group, which is a value to a filtered campaign. Initialize these properties through a constructor. For example:

public FacetItem(string id, string name, string iconClass = "")
{
    Id = id; // the id of facet item that display in the url as a value
    Name = name; // the name of facet item that shown in the facets widget
    IconClass = iconClass; // the css class that determining the icon of facet item. Default it's empty
}

Custom facets in Campaign view

First, you need a custom facet based on a campaign facet. In this article, we create a custom facet that

  • clears all built-in groups
  • has a facet group that filters by a campaign's last modified date. This user can choose last modified day, week, or month.
public class CustomFacet : CampaignFacet
{
    public CustomFacet(
        FacetFactory facetFactory, 
        IMarketService marketService, 
        LocalizationService localizationService
        ) : base(facetFactory, marketService, localizationService)
    {
        // Clear all built-in facet groups
        Groups.Clear();
        
        Groups.Add(facetFactory.CreateFacetGroup(
            "lastmodified",
            "Last Modified",
            new List() {
                facetFactory.CreateFacetItem("day", "Today"),
                facetFactory.CreateFacetItem("week", "A Week Ago"),
                facetFactory.CreateFacetItem("month", "A Month Ago")
            },
            new FacetGroupSettings(FacetSelectionType.Single, 0, true, true, false, Enumerable.Empty<string>())));
    }
}

This example shows that the last modified facet group is single selection with three options: day, week, or month.

Note: To show a item numbers that indicate how many campaigns are filtered, in FacetGroupSettings, set showMatchingItems to true.

new FacetGroupSettings(FacetSelectionType.Single, 0, true, true, true /*showMatchingItems*/, Enumerable.Empty<string>())

To use a CustomFacet, follow these steps.

1. Create a rest store named "customfacet" that returns a custom facet.

[RestStore("customfacet")]
public class CustomFacetStore : RestControllerBase
{
    FacetFactory _facetFactory;
    IMarketService _marketService;
    LocalizationService _localizationService;
    public CustomFacetStore(
        FacetFactory facetFactory, 
        LocalizationService localizationService, 
        IMarketService marketService)
    {
        _facetFactory = facetFactory;
        _marketService = marketService;
        _localizationService = localizationService;
    }

    public RestResult Get(string id, string facetString, ContentReference parentLink)
    {
        return Rest(new CustomFacet(_facetFactory, _marketService, _localizationService));
    }
}

Note: To show matching items, use the facet query handler to calculate matching numbers.

public RestResult Get(string id, string facetString, ContentReference parentLink)
{
    var customFacet = new CustomFacet(_facetFactory, _marketService, _localizationService);
    var contentLoader = ServiceLocator.Current.GetInstance();
    var facetQueryHandler = new FacetQueryHandler();
    facetQueryHandler.CalculateMatchingNumbers(
        contentLoader.GetChildren(parentLink), 
        customFacet.Groups, 
        facetString, 
        new GetContentsByFacet[] { new GetCampaignsByLastModified() });
    return Rest(customFacet);
}

2. Register the custom facet store and override the existing store "epi.commerce.facet". Create a module initializer. If an initializer exists, edit it.

define([
    "dojo/_base/declare",
    "epi/_Module",
    "epi/routes"
], function (
    declare,
    _Module,
    routes
) {
    return declare([_Module], {
        initialize: function () {
            this.inherited(arguments);

            var registry = this.resolveDependency("epi.storeregistry");
            // remove existing facet store
            if (registry.get("epi.commerce.facet")) {
                delete registry._stores["epi.commerce.facet"];
            }
            // register the custom facet
            registry.create("epi.commerce.facet", routes.getRestPath({ moduleArea: "app", storeName: "customfacet" }));
        }
    });
});

If you do not register a module initializer, you need to update or create the project's module.config file, as shown in the example below.

<?xml version="1.0" encoding="utf-8"?>
<module loadFromBin="false">
  <assemblies>
    <add assembly="EpiserverSite5" />
  </assemblies>
  <dojo>
    <paths>
      <add name="app" path="Scripts" />
    </paths>
  </dojo>
  <clientModule initializer="app.ModuleInitializer">
    <moduleDependencies>
      <add dependency="Commerce" type="RunAfter" />
    </moduleDependencies>
  </clientModule>
</module>

After following the steps above, Campaign view has a custom facet on the left side, and the built-in groups have disappeared. We can interact in the UI, but still need to filter campaigns.

4. Create a function to filter campaigns by last modified date, based on GetContentsByFacet.

public class GetCampaignsByLastModified : GetContentsByFacet
{
    public override string Key { get { return "lastmodified"; } }

    public override IEnumerable<IContent> GetItems(IEnumerable<IContent> items, IEnumerable<string> facets)
    {
        return items.Where(item => !(item is SalesCampaign) || AvailableFor((SalesCampaign)item, facets));
    }

    private bool AvailableFor(SalesCampaign item, IEnumerable<string> facets)
    {
        var changed = DateTime.UtcNow - item.Changed;
        var filterString = facets.FirstOrDefault();
        var isAvailable = false;
        switch (filterString)
        {
            case "day":
                isAvailable = changed.Days < 1;
                break;
            case "week":
                isAvailable = changed.Days < 7;
                break;
            case "month":
                isAvailable = changed.Days < 30;
                break;
        }
        return isAvailable;
    }
}

5. Customize the query to return GetCampaignsByLastModified, by overriding GetSalesCampaignChildrenQuery

[ServiceConfiguration(typeof(IContentQuery))]
public class CustomGetSalesCampaignChildrenQuery : GetSalesCampaignChildrenQuery
{
    public CustomGetSalesCampaignChildrenQuery(
        IContentQueryHelper queryHelper, 
        IContentRepository contentRepository, 
        LanguageSelectorFactory languageSelectorFactory, 
        CampaignInfoExtractor campaignInfoExtractor, 
        FacetQueryHandler facetQueryHandler) 
        : base(queryHelper, contentRepository, languageSelectorFactory, campaignInfoExtractor, facetQueryHandler){}

    public override int Rank
    {
        // need set rank to higher
        get { return 1000; }
    }

    protected override IEnumerable<GetContentsByFacet> FacetFunctions
    {
        get
        {
            return new GetContentsByFacet[] {
                new GetCampaignsByLastModified()
            };
        }
    }
}

Here is an example of the customized Campaign view.

  

Comments