Views: 20760
Number of votes: 14
Average rating:

Trying out PropertyList<T>

Since EPiServer 9.0.0, EPiServer.Core.PropertyList<T> has been available through the API. Keep in mind that this is  a pre-release API, as documented in the code: NOTE: This is a pre-release API that is UNSTABLE and might not satisfy the compatibility requirements as denoted by its associated normal version.

What is PropertyList<T>?
The new PropertyList type allows you to add editable list properties to your content. The property value will be stored in the database as serialized JSON objects. Below I’ve written an example on how you can use the new property type, with a  simple “Contact” class, which will be stored as an IList<Contact> on a page type.

listedit

 

The models
A very simple contact class. You could add other property types to it as well, such as ContentReference!

public class Contact
{
    public string Name { get; set; }
    public int Age { get; set; }
    [Display(Name = "Phone number")]
    public int PhoneNumber { get; set; }
}

 

A page type with a list of contacts:

public class StartPage : PageData
{
    public virtual IList<Contact> Contacts { get; set; }
 
    // ...
}

 

Register the property definition
If you start the site at this point, you’ll get an error message, saying “Type '<Your type>' could not be mapped to a PropertyDefinitionType.”
Register the type by using the PropertyDefinitionTypePlugin.

[PropertyDefinitionTypePlugIn]
public class ContactListProperty : PropertyListBase<Contact>
{
 
}

 

The ContactListProperty could inherit PropertyList directly, but in this case I have created a base class, PropertyListBase, that can be reused by other types. This inherits the abstract class PropertyList, and handles the JSON serialization of the property.

public class PropertyListBase<T> : PropertyList<T>
{
    public PropertyListBase()
    {
        _objectSerializer = this._objectSerializerFactory.Service.GetSerializer("application/json");
    }
    private Injected<ObjectSerializerFactory> _objectSerializerFactory;
 
    private IObjectSerializer _objectSerializer;
    protected override T ParseItem(string value)
    {
        return _objectSerializer.Deserialize<T>(value);
    }
 
    public override PropertyData ParseToObject(string value)
    {
        ParseToSelf(value);
        return this;
    }
}

 

CollectionEditorDescriptor
Finally, add an editor descriptor of type CollectionEditorDescriptor<T> to the property. This will give you a handy editor for the edit view, as shown in the images.

[EditorDescriptor(EditorDescriptorType = typeof (CollectionEditorDescriptor<Contact>))]
public virtual IList<Contact> Contacts { get; set; }

 

add

Nov 02, 2015

David.Knipe
(By David.Knipe, 11/2/2015 6:15:30 PM)

Nice write up, its great to see IList being supported and I hope people will find it useful :)!

Arve Systad
(By Arve Systad, 11/3/2015 1:41:13 AM)

Awesome! Been waiting for this! :)

What's the reason you're doing your own base class? Would it not work if you just inherit from PropertyList?

And should it not be possible to use that EditorDescriptor by default for any IList properties?

(yeyeye, I see it's pre release, just have ask :p)

Henrik Fransas
(By Henrik Fransas, 11/3/2015 9:06:55 AM)

Nice, also been waiting for this, will try it out!

Per Magne Skuseth
(By Per Magne Skuseth, 11/3/2015 9:50:24 AM)

@Arve, you could inherit directly, but ParseItem and ParseToObject are abstract methods, so you would have to implement them. That is way I created a base class. I suppose the reason for the abstract methods are that you may want to customize the serialization. It would be nice with a built in base class though.

Per Magne Skuseth
(By Per Magne Skuseth, 11/3/2015 2:33:10 PM)

why, not way.

Arve Systad
(By Arve Systad, 11/3/2015 4:52:17 PM)

Ah. Yeah, I guess a default base class that did this (Straightforward serialization/deserialization is pretty generic for POCO => JSON, eh?) would be nice.

Less pain to get to use this fancy new thing, and less "unneccessary mess" in the projects.

Khalid Aziz
(By Khalid Aziz, 11/3/2015 4:54:07 PM)

Do we have an idea when / which release this API might be stabilized by?

valdis
(By valdis, 11/3/2015 11:59:17 PM)

great success!

henriknystrom
(By henriknystrom, 11/4/2015 12:42:25 AM)

The reason for not implementing ParseItem and ParseToObject in the base class is that the main area they are used is for deserializing values entered in the textbox for default values in Admin UI. For this reason we didn't feel that JSON was a particularily user friendly format, for ContentReferenceListProperty we are using comma-separated content references instead. If you necessarily want to use JSON format here I would suggest overriding the ParseToSelf method instead so you end up with one JSON string rather than a comma-separated list of JSON strings. Note that you for full compatibility here also must ensure that the ToString() method on the list property mirrors the ParseToSelf/ParseItem serialization.

As a sidenote I can mention that there is built-in POCO <=> JSON serialization in the persistence layer

As for moving this API out of BETA, this is something that we will do when we feel confident that we don't need to do any breaking changes to the behavior. This could happen either by additional use by us internally or by great experiments like this that provides us with excellent feedback. In this particular case we weren't completely happy with the somewhat quirky requirement of the ParseItem method and this was one reasons for the PropertyList base class to remain in BETA while the PropertyContentRefereceList is fully supported.

Ted
(By Ted, 11/28/2015 3:03:53 PM)

I might be missing something, but PropertyList and the EditorDescriptor appear to assume POCO objects? Am I the only one needing pretty much the exact same behavior for native types like strings, integers and dates? :)

In other words, be able to support property definitions like the following:

public class PropertyStringList : PropertyList
    {
        // TODO Add missing members

        public override Type PropertyValueType => typeof(List);
    }

Markiemark
(By Markiemark, 12/9/2015 2:40:34 PM)

Very nice!

Adding and removing items works great, however modifying items does not seem to work. No changes are detected, so I'm not able to publish changes to the contact list.

The IsModified property of PropertyList always returns false.

Any idea how to solve this? 

henriknystrom
(By henriknystrom, 1/15/2016 12:10:18 PM)

@Ted, I might have answered you before on this, but the problem might be that you are overriding the PropertyValueType as you are showing. At least the PropertyList (excluding UI) should be able to handle simple types just fine.

@Mark, the built in modified tracking should work if the list items implements IModifiedTrackable. If not you will have to override it and provide your own comparison as it currently works.

Matthew Boniface
(By Matthew Boniface, 2/8/2016 11:15:22 PM)

I'm on EPiServer CMS 9.6.1 and cannot seem to get the above to work. I can see plenty of others commenting here have gotten much further so must be possible.. When I have the property like this:

public virtual IList CustomerSkus { get; set; }

I tried all of the above but keep getting the NotSupportedException with "The property data type IList can not be mapped to a MetaDataType".

And I've tried putting [BackingType(typeof(CustomerSkuListProperty))] and I then get NotSupportedException "The property data type Json can not be mapped to a MetaDataType".

I have definitely included the:

[PropertyDefinitionTypePlugIn]
public class CustomerSkuListProperty : PropertyListBase
{ }

And have copied the PropertyListBase exactly as in the post above so cannot figure why I get those exceptions..

Oh and the CustomerSku class is pretty simple like this:

public CustomerSku
{
    public string AccountNumer { get; set; }

    public string Sku { get; set; }
}

Anything I might have missed?

Matt Schiller
(By Matt Schiller, 7/18/2016 7:41:38 PM)

I just finally landed on the difference between the errors "could not be mapped to a PropertyDefinitionType" and "could not be mapped to a MetaDataType". 

If your object that you are adding the IList onto derives from BlockData and you don't have a PropertyList descriptor / attribute for it, it will throw that PropertyDefinitionType error. 

If your object you are adding the IList property onto derives from CatalogContent, it will throw the MetaDataType error even if you have a PropertyList descriptor. 

I am experimenting with a BackingType on the property to see if I can get it working, but it complains about not having a MetaDataType for Json, So seeing if I can add a custom Meta field for that.... 

David Elias
(By David Elias, 7/19/2016 1:29:09 AM)

Anyone know if this will work with AB Testing or Visitor Groups?

Jonas  Carlbaum
(By Jonas Carlbaum, 8/29/2016 4:46:24 PM)

We need to translate the propertylist attributes/properties.

  • Is it possible?
    • Has anybody done this?
    • Is it native?
    • Would it be the class name of the PropertyDefinitionTypePlugIn or the Templated item class in the language file as key?
    • What is the base path, tried /contenttypes and /properties etc. without any luck
  • Do we have to implement this ourselves?
    • Is PropertyListBase the best place to build a custom translation?

Has anyone got any experience or ideas around this?

AB
(By AB, 11/7/2016 6:09:20 PM)

It appears that this no longer works on the latest update (10.0.2).  Any ideas on how to get this to work?

Binraider
(By Binraider, 11/18/2016 4:07:02 PM)

AVOID THE TEMPTATION !!!!

Until this has been properly debugged it is a total trap, as if you change any of the classes in your PropertyList while data is in your database, or anything references it you get corrupted data and the database/application layer goes down.

AVOID THE TEMPTATION !!!!

Henrik Sjölin
(By Henrik Sjölin, 1/18/2017 12:43:35 PM)

As Binraider mentioned there is a problem with this.

I did the following:

I added a XhtmlString property named MainBody to the model Answer and created a PropertyList-class as described above. Everything worked like a charm. Later, during developement, I realized I needed to change the type of MainBody to a standard string instead of XhtmlString.

When I did this, the site bricked with the following error:

System.MissingMethodException
Method not found: EPiServer.Core.XhtmlString MyApp.Answer.get_MainBody()

I tried to remove all items in the list and replaced them with new items but it still did not work. 

When you change type or name of a property of a standard page or block in EPiServer you are normally able to administer the property type and name through the EPiServer Admin, but this is not the case when using propertylist based properties. Not what I am aware of anyway.

Anyone else having this issue and know how to resolve this? 

Per Magne Skuseth
(By Per Magne Skuseth, 1/18/2017 1:16:32 PM)

@Henrik, typically this will happen if your view is cached and still think your property is an XhtmlString - which will cause it to give that error. Try modifying your view, save it, and load the page again. It should work.
I've never had any issues when changing types - including XhtmlStrings.

Henrik Sjölin
(By Henrik Sjölin, 1/18/2017 2:09:10 PM)

@Per Thanks! That solved the problem.

Per Magne Skuseth
(By Per Magne Skuseth, 1/18/2017 2:13:07 PM)

Great! I'm guessing that is what happened to Binraider too. Can't say for sure though.

Jonas  Carlbaum
(By Jonas Carlbaum, 1/25/2017 2:10:33 PM)

Hi again!

Is there no one out there that has any kind of idea on how to solve the issue I posted about in previous comment on this post?

We need to translate the PropertyList-items properties. This should have become something that's needed on many sites now developed using PropertyList, so I hoped that someone have any ideas around this, and hopefully a solution... Simple or advanced... ;)

Kai de Leeuw
(By Kai de Leeuw, 4/11/2017 1:54:04 PM)

[Display(Name="/path/to/lang/resource")]

ngoanmai
(By ngoanmai, 7/25/2017 12:27:20 PM)

@Per Magne Skuseth: Thanks for the great article :).

I have issue with List property when my model contains the xhtmlString. 

public class TextInformationModel
   {
       public string Title { get; set; }
 
       public XhtmlString Content { get; set; }
   }

But the display for the this xhtmlstring is not really good.

Is there anyone getting this issue also?


suresh kalyanasundaram
(By suresh kalyanasundaram, 9/22/2017 4:31:30 PM)

I've tried this using block type. I want the text fields to show and add them into list items as like above in this article

I've copied the complete code below for your reference. Please let me know if i am wrong in my code below to make it work as same like above.

//Block class

public class KeyValueBlock
{
public virtual string Key { get; set; }

public virtual XhtmlString Content { get; set; }

}

//Block type

public class KeyValueContentListBlock : BlockData
{
[CultureSpecific]
[Display(
Name = "KeyValue",
Description = "Key Value List",
GroupName = SystemTabNames.Content,
Order = 1)]
[EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor))]

public virtual IList KeyValues { get; set; }
}

//Property class under model and property folders

[PropertyDefinitionTypePlugIn]
public class KeyValuePropertyList : PropertyList
{
protected override KeyValueBlock ParseItem(string value)
{
return JsonConvert.DeserializeObject(value);
}

public override PropertyData ParseToObject(string value)
{
ParseToSelf(value);
return this;
}
}

//view file

@model KeyValueContentListBlock

@if (Model != null && Model.KeyValues != null)
{
foreach (var element in Model.KeyValues)
{
@Html.DisplayTextFor(m => element.Key)
@Html.DisplayTextFor(m => element.Content)
}
}

Raja Chandran
(By Raja Chandran, 12/22/2017 2:04:01 PM)

public class SocialMediaItem : IHasImage
{
[UIHint(UIHint.Image)]
[Required]
[Display(Name = "Social Media Image", Order = 10)]
public ContentReference Image { get; set; }

[Required]
[Display(Order = 20)]
public string Name { get; set; }


[Required]
[Display(Order = 30)]
public Url Url { get; set; }
}

[PropertyDefinitionTypePlugIn]
public class SocialMediaItemListProperty : PropertyListBase
{
}

public class PropertyListBase : PropertyList
{
private readonly IObjectSerializer _objectSerializer;

private Injected _objectSerializerFactory;

public PropertyListBase()
{
this._objectSerializer = this._objectSerializerFactory.Service.GetSerializer("application/json");
}

protected override T ParseItem(string value)
{
return this._objectSerializer.Deserialize(value);
}

public override PropertyData ParseToObject(string value)
{
this.ParseToSelf(value);

return this;
}
}

This code was working fine without any issues in EPiServer 10.10.4 version.

However after upgrading to EPiServer 11, the URL property data is not saving any data and throwing an exception. The Breaking changes CMS 11 to use JsonConverter. Could you please tell me how to use JsonConverter to the existing propertylist and what should be done in the PropertyListbase to save Url property data.

kedar more
(By kedar more, 1/16/2018 4:07:34 PM)

We are also facing the same issue. We used property list and it was working properly till we upgraded Episerver version to 11.3.1 (earlier it was 10.10.1).

We have used JSonconvert but whenever we update values in property list it is not saving value for url property.

Used the below code in PropertyListBase class.

protected override T ParseItem(string value)
{
  return JsonConvert.DeserializeObject(value);
}

Any help would be appreciated.

kedar more
(By kedar more, 1/18/2018 12:25:02 PM)

This has been resolved now.

Please refer article for more details-

https://world.episerver.com/forum/developer-forum/-Episerver-75-CMS/Thread-Container/2017/12/new-propertylistlttgt-episerver-11/ 

nalinb
(By nalinb, 4/17/2018 3:19:26 PM)

Each row in the list has four options by default (Edit, Move up, Move down, and Delete). How can I remove the delete option, and keep only other three options.

What I am trying to implement should have only soft delete (using a property for that).

Any help would be appreciated.

karthikeyan ponnusamy
(By karthikeyan ponnusamy, 6/8/2018 7:13:39 PM)

Hi,

I created the property like below,But the list values are not displying in epi all properties and already added values not displayed

public class VinDecodeMakeModelMapping
{
[Display(Name = "Make")]
public string Make { get; set; }

[Display(Name = "Models Mappings")]
public IEnumerable Models { get; set; }
}

-----------

[PropertyDefinitionTypePlugIn]
public class VinDecodeMakeModelMappingsListBase : PropertyListBase
{
}

---------

public class PropertyListBase : PropertyList
{
private readonly IObjectSerializer objectSerializer;

private Injected objectSerializerFactory;

public PropertyListBase()
{
this.objectSerializer = this.objectSerializerFactory.Service.GetSerializer("application/json");
}

protected override T ParseItem(string value)
{
return this.objectSerializer.Deserialize(value);
}

public override PropertyData ParseToObject(string value)
{
this.ParseToSelf(value);

return this;
}
}

--------

I created a property on my page

  [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor))]
public virtual IList VinDecodeMakeModel { get; set; }

------

check link my epi all property page

https://drive.google.com/open?id=1G9PnDCDJs3arlPMkRlZyXuV2vR7VuEFV

Please reply asap,

Advance thanks

Antti Alasvuo
(By Antti Alasvuo, 6/11/2018 9:35:42 PM)

@karthikeyan, you should mention that you are using Episerver CMS 10.3.1 and you can't upgrade as you mentioned in the other thread you started about the issue.

Please login to comment.