HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

List properties

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; 
}
Screenshot of a ContactBlock list property editor where showing collapsed list items

Each list item looks the same when collapsed: just the type name. Override this by specifying which property renders the list item header.

[ListItemHeaderProperty(nameof(Blocks.ContactBlock.Heading))]
public virtual IList<ContactBlock> Contacts { 
  get; 
  set; 
}

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

public virtual IList<string> ListOfStrings { 
  get; 
  set; 
}
Screenshot of an IList of strings property editor where showing editable string list items
public virtual IList<int> ListOfIntegers { 
  get; 
  set; 
}
Screenshot of an IList of integers property editor where showing editable integer list items
public virtual IList<DateTime> ListOfDates { 
  get; 
  set; 
}
Screenshot of an IList of DateTime property editor where showing editable date and time list items
public virtual IList<XhtmlString> ListOfXhtml { 
  get; 
  set; 
}
Screenshot of an IList of XhtmlString property editor where showing editable rich text list items

Define a list property with a custom type inside. Do not use the custom type as a block in the system. Only an editor and descriptor for a single property are needed; the system applies them when the property appears within a list. For example, if there is a custom editor for a type GeoCoordinate 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 inherit from BlockData, or you must provide a custom BackingType.

[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.

Provide a custom UIHint to the list item. For example, create a list of images or video files:

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

...which is rendered like this:

Screenshot of an IEnumerable of ContentReference property editor where showing an image list with thumbnails
📘

Note

The property type does not have to be IList. The Optimizely descriptors match the IList interface and all its base interfaces, so IEnumerable or ICollection also work. 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

Apply validation to the list itself and to individual items. Use ListItemsAttribute to control the number of items in the list:

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

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; 
}

Provide custom metadata and validators to the items inside the list. For example, if a List<ContactBlock> is defined, create a metadata extender just for ContactBlock or a custom validator of ContactBlock. The system applies it to each instance created within the list property.

Render a list

Display templates control how list properties appear on the page.

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.

The Alloy package includes an example of a Display Template:

@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)

The on-page edit displays the following result:

Screenshot of the UniqueSellingPoints property rendered on-page where showing editable selling point items

The same approach applies to more complex types:

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

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

Create a custom display template for the whole list if a simple ul or list is insufficient.