HomeGuidesAPI Reference
Submit Documentation FeedbackJoin Developer CommunityOptimizely GitHubOptimizely NuGetLog In

List Properties

This topic describes how to model lists of multiple values.

Most property types in Optimizely Content Management System (CMS) can be modeled as lists, including types modeled as block types. For example given that there is a block type modeled as:

[ContentType(AvailableInEditMode = false, GUID = "38d57768-e09e-4da9-90df-54c73c61b270")]  
public class ContactBlock : BlockData  
{  
   //block properties  
}

then other Content models can define lists of the block type as:

public virtual IList<ContactBlock> Contacts { get; set; }

The idea is the same as with the List<T>, you add the list as a new property to your model, and the default descriptors match IList  type and render the correct editor. For example:

public virtual IList<string> ListOfStrings { get; set; }

public virtual IList<int> ListOfIntegers { get; set; }

public virtual IList<DateTime> ListOfDates { get; set; }

public virtual IList<XHtmlString> ListOfXhtml { get; set; }

You can also define a list property with a custom type inside, which you should not use as a block in the system. The only thing you need is an editor and descriptor for a single property and it will be used if the property is used within a list. For example if there is a custom editor for a type GeoCoordinate which is defined like this:

public virtual IList<GeoCoordinate> Contacts { get; set; }

...where GeoCoordinate is...

public class GeoCoordinate
{
   public decimal Latitude { get;set; }
   public decimal Longtitude { get;set; }
   public int Zoom { get;set; }
}

...and then there is a custom type descriptor for GeoCoordinate which defines a custom editor...

[EditorDescriptorRegistration(TargetType = typeof(GeoCoordinate))]
public class GeoCoordinateEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
    {
        ClientEditingClass = "alloy/editors/GoogleMapsPicker";

        base.ModifyMetadata(metadata, attributes);
    }
}

...GeoCoordinate must be inherited from BlockData or a custom BackingType has to be provided.

[ContentType(AvailableInEditMode = false, GUID = "11d57768-e09e-4da9-90df-54c73c61b270")]  
public class GeoCoordinate : BlockData
{
   public virtual decimal Latitude { get;set; }
   public virtual decimal Longtitude { get;set; }
   public virtual  int Zoom { get;set; }
}

📘

Note

The AvailableInEditMode attribute and virtual modifier are added to all model properties.

You can provide a custom UIHint to the list item. For example, you can create a list of images or video files:

[UIHint(UIHint.Image)]
public virtual IEnumerable<ContentReference> Images { get; set; }

...which will be rendered like this:

📘

Note

The property type does not have to be IList. The Optimizely descriptors are configured to match the IList interface but they also cover all base ones, so if you prefer to use IEnumerable or ICollection you can do so. All the following declarations are matched by the Value List descriptor.

public virtual IEnumerable<string> EnumerableStrings { get; set; }
    public virtual ICollection<string> CollectionOfStrings { get; set; }
    public virtual IList<string> ListOfStrings { get; set; }

Validate a list

You can apply validation to both the list itself and to the individual items, and you can use ListItemsAttribute to control the number of items in the list:

[ListItems(5)]
public virtual IList<int> Max5Items { get; set; }

You can apply the following attributes to individual items:

[ItemRangeAttribute(1, 10)]
public virtual IList<int> ItemsBetween1And10 { get; set; }
    
[ItemRegularExpression("[a-zA-Z]*")]
public virtual IList<string> LettersOnly { get; set; }
    
[ItemStringLength(3)]
public virtual IList<string> ListOfAcronyms { get; set; }

You can also provide custom metadata and validators to the items inside the list. For example, if there is a List<ContactBlock> defined, you can create a metadata extender just for ContactBlock or have a custom validator of ContactBlock and it is applied to each instance created within the list property.

Render a list

To render a list:

public virtual IList<string> UniqueSellingPoints { get; set; }

To use the PropertyFor helper method you must define a specific Display template, and to do that you must first attach a UIHint to your property:

[UIHint("StringsCollection")]
public virtual IList<string> UniqueSellingPoints { get; set; }

Then add a StringsCollection.cshtml file to the ~/Views/Shared/DisplayTemplates folder.

You can see an example of a Display Template in the Alloy package:

@model IEnumerable<string>
    @if(Model != null && Model.Any())
      {
        <ul>
            @foreach(var stringValue in Model)
              {
                <li>@stringValue</li>
              }
        </ul>
      }

Add a UniqueSellingPoints property to your page template:

@Html.PropertyFor(x => x.CurrentPage.UniqueSellingPoints)

And you will see the following result in on-page edit:

The same with more complex types:

public virtual IList<ContactBlock> ContactBlocks { get; set; }

The system looks for a partial view for ContactBlock inside ~/Views/Shared/DisplayTemplates and it will render the items as ul -> li by default.

However, you can create a custom display template for the whole list, if simple ul/list is not enough.