Per Magne Skuseth
Nov 2, 2015
  31658
(14 votes)

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

Comments

Nov 2, 2015 05:15 PM

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

Arve Systad
Arve Systad Nov 3, 2015 12:41 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
Henrik Fransas Nov 3, 2015 08:06 AM

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

Per Magne Skuseth
Per Magne Skuseth Nov 3, 2015 08:50 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
Per Magne Skuseth Nov 3, 2015 01:33 PM

why, not way.

Arve Systad
Arve Systad Nov 3, 2015 03:52 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.

kaziz
kaziz Nov 3, 2015 03:54 PM

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

valdis
valdis Nov 3, 2015 10:59 PM

great success!

henriknystrom
henriknystrom Nov 3, 2015 11:42 PM

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
Ted Nov 28, 2015 02:03 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
Markiemark Dec 9, 2015 01:40 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
henriknystrom Jan 15, 2016 11:10 AM

@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
Matthew Boniface Feb 8, 2016 10:15 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
Matt Schiller Jul 18, 2016 05:41 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
David Elias Jul 18, 2016 11:29 PM

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

Jonas Carlbaum
Jonas Carlbaum Aug 29, 2016 02:46 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?

Adam B
Adam B Nov 7, 2016 05:09 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
Binraider Nov 18, 2016 03:07 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
Henrik Sjölin Jan 18, 2017 11:43 AM

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
Per Magne Skuseth Jan 18, 2017 12:16 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
Henrik Sjölin Jan 18, 2017 01:09 PM

@Per Thanks! That solved the problem.

Per Magne Skuseth
Per Magne Skuseth Jan 18, 2017 01:13 PM

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

Jonas Carlbaum
Jonas Carlbaum Jan 25, 2017 01:10 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
Kai de Leeuw Apr 11, 2017 11:54 AM

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

ngoanmai
ngoanmai Jul 25, 2017 10:27 AM

@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
suresh kalyanasundaram Sep 22, 2017 02:31 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@lbi.com
raja.chandran@lbi.com Dec 22, 2017 01:04 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
kedar more Jan 16, 2018 03:07 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
kedar more Jan 18, 2018 11:25 AM

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/ 

Nalin Bhayana
Nalin Bhayana Apr 17, 2018 01:19 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.

karthik
karthik Jun 8, 2018 05:13 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
Antti Alasvuo Jun 11, 2018 07:35 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.

Godwin Ajit Dhas Paul Thangam
Godwin Ajit Dhas Paul Thangam Feb 7, 2020 10:26 PM

@ngoanmai were you able to resolve your issue of the Property list display of the xhtmlstring as raw HTML in the list mode ?

Please login to comment.
Latest blogs
Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog

ExcludeDeleted(): Prevent Trashed Content from Appearing in Search Results

Introduction In Optimizely CMS, content that is moved to the trash can still appear in search results if it’s not explicitly excluded using the...

Ashish Rasal | Nov 7, 2024

CMS + CMP + Graph integration

We have just released a new package https://nuget.optimizely.com/package/?id=EPiServer.Cms.WelcomeIntegration.Graph which changes the way CMS fetch...

Bartosz Sekula | Nov 5, 2024

Block type selection doesn't work

Imagine you're trying to create a new block in a specific content area. You click the "Create" link, expecting to see a CMS modal with a list of...

Damian Smutek | Nov 4, 2024 | Syndicated blog