We are extensive users of the feature of implementing custom selection factories by using the EditorDescriptor
-attribute in Episerver.
The use-case is quite simple and provides the editor with the ability to single select a value within multiple alternatives using a drop down. But as the selection stores grow, the projects gets unnecessary bloated with duplicate code.
Here's a take on that.
Key/Values for the win
First we need to abstract some things. The ISelectionFactory
can easily be translated to a key-value store, and the ISelectItem
object as a key/value item. (documentation)
Therefore, the Dictionary<string, string>
is optimal in this use case.
Although this solution is fairly straight forward, this is the "mind-twisting" aspect.
Our Key
will be our Value
when binding our ISelectItem
with our Dictionary<string, string>
.
Why? Because it's the points in each object which need to be unique.
Notation | Dictionary | ISelectItem |
---|---|---|
Need to be unique | Key | ISelectItem.Value |
Generic | Value | ISelectItem.Text |
In Episerver, the values of the "Text"-field is used as presentation, and "Value" as the value which is aimed to be saved in our data store.
Our custom selection factory
Our generic but custom selection factory takes a generic object of T
which we assume is of type Dictionary<string, string>
. This is a good point to implement some logging and error handling.
When we have a valid instance, simply loop through the items and yield
as your common SelectionFactory would.
Please note the binding of SelectionItem.Value = Setting.Key
as described previously.
The Editor Descriptor
Now create a generic EditorDescriptor which allows us to provide a custom selection factory.
SelectionFactoryType = typeof(CustomSelectionFactory<TSelectionFactory>)
is used to point out our custom selection factory. The value passed via the attribute is passed down to our CustomSelectionFactory
object.
The Editor Descriptor attribute
The fun part with the built-in EditorDescriptor
attribute is that we can define a new type with the EditorDescriptionType
argument.
This means that we can call it like this: [EditorDescriptor(EditorDescriptorType = typeof(CustomSelectionFactoryEditorDescriptor<TSelectionFactory>))]
to decorate our property on our Episerver model.
Bind with data
A trivial example to bind this newly created functionality with some fine data is to create a class which inherits Dictionary<string, string>
.
And then simply decorate our property like this:
[EditorDescriptor(EditorDescriptorType = typeof(CustomSelectionFactoryEditorDescriptor<SelectionSettings>))]
public virtual string MySelector { get; set; }
The fun part with this is that you simply can take an existing data source, such as an XML-file or a JSON data object, in your project and reuse as a selection factory. All you need to do is to create a new "SelectionSettings" class or bind a current object inheriting Dictionary<string, string>
and rock and roll.
Closing comments
When writing this, several alternative solutions come to mind, but all have one thing in common; it all requires an agreement on the data structure and simply boils down to a key/value store.
In our implementation we also have overwritten the epi-cms/contentediting/editors/SelectionEditor
with our own so we can pass HTML-formatted values via our selection factories. See blogpost "Make Episerver SelectionEditor support HTML-labels" for further implementation.