HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev 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; 
}

List of content block

Each list item looks the same when collapsed – you see just the type name. However, you can override that and specify which property should be used when rendering list item header.

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

The idea is the same as with the List<T>you add the list as a 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; 
}

list of string

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

enumerable of int

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

list of datetime

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

list of XhtmlString

You can also define a list property with a custom type inside, which you should not use as a block in the system. You only need an editor and descriptor for a single property, and it is 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 is rendered like this:

enumerable of ContentReference

📘

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 the list itself and 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)

You will see the following result in the on-page edit:

UniqueSellingPoints property

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 a simple ul or list is insufficient.