Custom Editor UI

Ucommerce has built-in editors for most use cases. If they do not cover a specific use case, it is possible to create a custom editor by utilizing our Custom UI component system.

Using a custom component as an editor draws on the power of our definition system and our custom UI components system.

To get started, create a new data type in the database. The DefinitionName column on the data type is the property that maintains a connection to a custom component. Here is a code example of this:

var dataType = new DataTypeEntity
{
    Name = "MyCustomField",
    Nullable = true,
    ValidationExpression = "",
    BuiltIn = false,
    DefinitionName = "CustomCriterionField01",
};

Use this new data type, to either create a new definition for the entity you want to extend or add a definition field to an existing definition.

An example of creating a definition with definition fields can be found in our Extending Criteria or Product Definitions & Fields documentation. They use criterion and product entities as examples, but the definition system can be used for any entity supported by the definition system.

The key of the custom UI component has to match the DefinitionName of the data type entity. Custom UI components in general are discussed further in Custom UI Components.

When a definition field, using a data type with a DefinitionName property matching the name of a registered component exists, the custom UI editor is functional and will show up in the backoffice UI. Below is an example of a class containing a background service that creates and saves a new definition, similar to the example in the Product Definitions & Fields documentation. The class also has a method for registering the new custom components and the background service in the DI container. The AddCustomEditor method can then be used to register everything needed for a new definition to show up.

/// <summary>
/// Class containing setup of a new Custom Criterion Definition
/// with custom UI components.
/// </summary>
public static class CustomCriterionCustomUiComponents
{
    private const string CUSTOM_EDITOR01 = "CustomCriterionField01";
    private const string CUSTOM_EDITOR02 = "CustomCriterionField02";
    // The id of the definition type to map to in your database
    private const int DEFINITION_TYPE_ID = 5434834;

    /// <summary>
    /// Method for registering a new criterion definition with custom UI components.
    /// </summary>
    public static IUcommerceBuilder AddCustomEditor(this IUcommerceBuilder builder)
    {
        builder.Services
            .AddHostedService<SetupCustomCriterionUsingCustomUiComponents>();
        builder.AddCustomComponentWithPath(
            CUSTOM_EDITOR01, 
            $"{CUSTOM_EDITOR01} Header", 
            "result-component", 
            "webComponentSample.js");
        builder.AddCustomComponentWithPath(
            CUSTOM_EDITOR02, 
            $"{CUSTOM_EDITOR02} Header", 
            "result-component", 
            "webComponentSample.js");
        builder.AddCustomComponentWithPath(
            CUSTOM_EDITOR02, 
            $"{CUSTOM_EDITOR02} second Header", 
            "result-component", 
            "webComponentSample.js");
        return builder;
    }

    public class SetupCustomCriterionUsingCustomUiComponents : BackgroundService
    {
        private readonly IServiceProvider _serviceProvider;

        public SetupCustomCriterionUsingCustomUiComponents(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await using var asyncScope = _serviceProvider.CreateAsyncScope();
            var dbContext = asyncScope.ServiceProvider
                .GetRequiredService<UcommerceDbContext>();

            // Guard to ensure code is only run once
            if (dbContext.Set<DefinitionEntity>()
                    .FirstOrDefault(x => x.Name == "Custom Criterion Definition") is not null)
            {
                return;
            }

            // Set up data using dbContext
            var firstDataType = new DataTypeEntity
            {
                Name = "First Custom Editor Type",
                Nullable = true,
                ValidationExpression = "",
                BuiltIn = false,
                DefinitionName = CUSTOM_EDITOR01,
            };

            // Set up data using dbContext
            var secondDataType = new DataTypeEntity
            {
                Name = "Second Custom Editor Type",
                Nullable = true,
                ValidationExpression = "",
                BuiltIn = false,
                DefinitionName = CUSTOM_EDITOR02
            };
            dbContext.Add(firstDataType);
            dbContext.Add(secondDataType);
            var defFields = new List<DefinitionFieldEntity>();

            defFields.Add(new DefinitionFieldEntity
            {
                Name = "First Custom Field",
                DefaultValue = "",
                DataType = firstDataType,
                DisplayOnSite = true,
                RenderInEditor = true
            });
            defFields.Add(new DefinitionFieldEntity
            {
                Name = "Second Custom Field",
                DefaultValue = "",
                DataType = secondDataType,
                DisplayOnSite = true,
                RenderInEditor = true
            });

            dbContext.Set<DefinitionEntity>()
                .Add(new DefinitionEntity
                {
                    BuiltIn = false,
                    Description = "Custom Criterion Definition Description",
                    Name = "Custom Criterion Definition",
                    DefinitionTypeId = DEFINITION_TYPE_ID,
                    DefinitionFields = defFields
                });

            await dbContext.SaveChangesAsync(stoppingToken);
        }
    }
}

Definitions and custom UI components are cached so a hard reload of the browser might be necessary to see the new editors.

Last updated