IEnumerable<IPublishedContent> to single IPublishedContent - DittoValueResolver or TypeConverter?
Hey all,
I'm using a MNTP as a way of filtering content out from a tree picker of a specific document type alias.
When using the Umbraco Core Property Value Converters this converts the value to a IEnumerable<IPublishedContent>. I'd ideally like a single IPublishedContent then use a TypeConverter to get whatever I need from it.
Would it be best to use a DittoValueResolver to resolve the value of the achieve this or leave the logic in the TypeConverter?
It feels like DittoValueResolver is the right way to go. However, I'd want to reuse the logic from an UmbracoPropertyValueResolver for gathering the property value first.
I love using Ditto but I'm a bit out of the loop when it comes to stuff since Value Resolvers!
Then with the custom/inherited ValueResolver, you can call the base.ResolveValue(), (of UmbracoPropertyValueResolver), which should return you the IEnumerable<IPublishedContent> value... from that, take the first (or single) value.
Hey Lee,
That's exactly what I'd like to do! I think the part I'm struggling with is retaining the functionality of the UmbracoPropertyAttribute whilst also adding the link to my custom resolver.
Ah! I've just noticed the problem... that the UmbracoPropertyAttribute is tied to the UmbracoPropertyValueResolver, without any way to override it! :-(
We'd need to have an overload on the constructor to enable a different ValueResolver to be used.
e.g.
public UmbracoPropertyAttribute(Type resolver)
: base(resolver)
{ }
That way you can have your SinglePublishedContentUmbracoPropertyAttribute class like so...
public class SinglePublishedContentUmbracoPropertyAttribute
: UmbracoPropertyAttribute
{
public SinglePublishedContentUmbracoPropertyAttribute()
: base(typeof(SinglePublishedContentValueResolver))
{ }
}
Of course that means editing Ditto core... feel free to send a PR, or open an issue and I'll find some time to add it in.
The alternative is to copy the guts of the UmbracoPropertyValueResolver class and use that?
For now the case is fairly simple, my use case is for stuff like specifying a target search page for a search form on a website header or in this case where a jobs form posts too.
I'm in the midst of moving over to a new laptop so I'll set up an issue on the repo. Cheers, Lee! I thought I was going barmy or doing something wrong.
I'm thinking that a Value Resolver might not be the best option here. Personally I'm thinking that tweaking the property value converter might be the best tact.
Change the MNTP property value converter to detect if the target type is IEnumerable
I'm thinking that if all you want to do is wrap the UmbracoPropertyAttribute so as to support all the same options but just change the type coming from the PVC, you should either fix the PVC, or use a TypeConverter?
For the simplest approach, with the least amount of custom code, I'd advise this...
Remove the "Umbraco Core Property Value Converters" library, if you're not using it elsewhere, then you don't need it.
If all you want is the node's Url property, (taken from here), then try this...
Create a custom TypeConverter that inherits from DittoPickerConverter, then you can override the ConvertFrom method...
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var result = base.ConvertFrom(context, culture, value);
if (result is IPublishedContent)
return ((IPublishedContent)result).Url;
if (result is IEnumerable<IPublishedContent>)
return ((IEnumerable<IPublishedContent>)result).FirstOrDefault().Url;
return value;
}
Hey Lee,
This makes a lot of sense. I can't think of any other situation where I'd want anything more than the URL so this probably as complex as the code need be.
As for removing the "Umbraco Core Property Value Converters" project I'll have to see if there is any knock on effect as I'll be using Archetype too.
I'm trying to use Lee's TypeConverter code above to map an IPublishedContent returned from MNTP to a custom model however I've come across a stumbling block, a media picker property on the selected MNTP node does not seem to be getting mapped correctly.
The following is my models and the typeconverter code (snipped for brevity). On the "Open Day" document type I have an MNTP property that allows one "University" node to be selected. The University document type has a media picker property on it to select the university's logo.
public class OpenDay : PublishedContentModel
{
public OpenDay(IPublishedContent content)
:base(content)
{}
[TypeConverter(typeof(UniversityTypeConverter))]
public University AssociatedUniversity { get; set; }
}
public class University : PublishedContentModel
{
public University(IPublishedContent content)
: base(content)
{ }
public IPublishedContent UniversityLogo { get; set; }
}
public class UniversityTypeConverter : DittoPickerConverter
{
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
var result = base.ConvertFrom(context, culture, value);
if (result is IPublishedContent)
return ((IPublishedContent)result).As<University>() ;
if (result is IEnumerable<IPublishedContent>)
return ((IEnumerable<IPublishedContent>)result).FirstOrDefault().As<University>();
return value;
}
}
When I map the "Open Day" node to the model it maps all the properties of both OpenDay and University except for the UniversityLogo property. Does anyone know why this property is no being set.
p.s. I have the Umbraco Property Value Converters installed.
OK, to clarify - the other properties of AssociatedUniversity are populated, except for UniversityLogo?
Just to get it out the way, I'd check for any typos in property-aliases, (sorry, it can happen) :-)
After that, the "universityLogo" property (media-picker) would contain an ID (int), which I'd assume that the 'Umbraco Property Value Converters' package would be picking up and converting to an IPublishedContent.
So looking from your code, everything looks to be wired up correctly.
Let me know if there is a typo ... I'm hoping I'm wrong though.
Thinking about it, you might be able to remove the UniversityTypeConverter all together.
With Ditto, if a property returns a single or list of IPublishedContent nodes, then it will check if the POCO's target property is a matching type - if not, then Ditto will attempt to map it to the target type.
I initially tried it without the TypeConverter and it didn't work which is why I added it.
Not sure exactly what I did as I was playing around trying to work out the problem but it seems to work now. I can only think there was some issue with the cache as I didn't change any code, just made sure all the Open Day and University pages had properties set and re-saved them.
IEnumerable<IPublishedContent> to single IPublishedContent - DittoValueResolver or TypeConverter?
Hey all,
I'm using a MNTP as a way of filtering content out from a tree picker of a specific document type alias.
When using the Umbraco Core Property Value Converters this converts the value to a
IEnumerable<IPublishedContent>
. I'd ideally like a singleIPublishedContent
then use a TypeConverter to get whatever I need from it.Would it be best to use a DittoValueResolver to resolve the value of the achieve this or leave the logic in the TypeConverter?
It feels like DittoValueResolver is the right way to go. However, I'd want to reuse the logic from an UmbracoPropertyValueResolver for gathering the property value first.
I love using Ditto but I'm a bit out of the loop when it comes to stuff since Value Resolvers!
Thanks,
Jamie
Hi Jamie,
If I understand correctly, you want to get the first (or single) value from the list of MNTP nodes, (
IPublishedContent
)?ValueResolvers would be the way to go.
To reuse the
UmbracoPropertyValueResolver
, you'd need to inherit from it... and also inherit fromUmbracoPropertyAttribute
as both are closely tied together.Then with the custom/inherited ValueResolver, you can call the
base.ResolveValue()
, (ofUmbracoPropertyValueResolver
), which should return you theIEnumerable<IPublishedContent>
value... from that, take the first (or single) value.Hope that makes sense?
Cheers,
- Lee
Hey Lee,
That's exactly what I'd like to do! I think the part I'm struggling with is retaining the functionality of the UmbracoPropertyAttribute whilst also adding the link to my custom resolver.
Here's what I have so far.
https://gist.github.com/jamiepollock/fc69044cf874bd838d76
The code doesn't actually work mind.
Thanks,
Jamie
Ah! I've just noticed the problem... that the
UmbracoPropertyAttribute
is tied to theUmbracoPropertyValueResolver
, without any way to override it! :-(https://github.com/leekelleher/umbraco-ditto/blob/develop/src/Our.Umbraco.Ditto/ComponentModel/Attributes/UmbracoPropertyAttribute.cs#L16
We'd need to have an overload on the constructor to enable a different ValueResolver to be used.
e.g.
That way you can have your
SinglePublishedContentUmbracoPropertyAttribute
class like so...Of course that means editing Ditto core... feel free to send a PR, or open an issue and I'll find some time to add it in.
The alternative is to copy the guts of the
UmbracoPropertyValueResolver
class and use that?Cheers,
- Lee
Indeed! That's the missing link. :)
For now the case is fairly simple, my use case is for stuff like specifying a target search page for a search form on a website header or in this case where a jobs form posts too.
I'm in the midst of moving over to a new laptop so I'll set up an issue on the repo. Cheers, Lee! I thought I was going barmy or doing something wrong.
Issue created!
Thanks,
Jamie
It's a case of that we never fully know how those features would be used by developers... it's all progression! :-)
So long as we're all learning, we all benefit along the way. :)
I'm thinking that a Value Resolver might not be the best option here. Personally I'm thinking that tweaking the property value converter might be the best tact.
Change the MNTP property value converter to detect if the target type is IEnumerable
I'm thinking that if all you want to do is wrap the UmbracoPropertyAttribute so as to support all the same options but just change the type coming from the PVC, you should either fix the PVC, or use a TypeConverter?
@Jamie - are you using any other PVCs from the "Umbraco Core Property Value Converters" library?
If not, then as Matt suggests would be a simpler option, as it converts the value early enough in the pipeline.
Cheers,
- Lee
Just in case you need an example of how to de-register an associated PVC.
... and the docs for creating your own PVC:
https://our.umbraco.org/Documentation/Extending/Property-Editors/value-converters
Cheers,
- Lee
@lee, I'm not - so I guess I could go down this route? It feels like a MNTP should convert to single if only one is selected.
Hey Jamie, I'm not sure where was best to reply, here or the GitHub issue comments?
I'll go here :-)
For the simplest approach, with the least amount of custom code, I'd advise this...
Remove the "Umbraco Core Property Value Converters" library, if you're not using it elsewhere, then you don't need it.
If all you want is the node's
Url
property, (taken from here), then try this...Create a custom TypeConverter that inherits from
DittoPickerConverter
, then you can override theConvertFrom
method...Note: I haven't actually tested this.
Hope this helps?
Cheers,
- Lee
Hey Lee,
This makes a lot of sense. I can't think of any other situation where I'd want anything more than the URL so this probably as complex as the code need be.
As for removing the "Umbraco Core Property Value Converters" project I'll have to see if there is any knock on effect as I'll be using Archetype too.
Thanks,
Jamie
I'm trying to use Lee's TypeConverter code above to map an IPublishedContent returned from MNTP to a custom model however I've come across a stumbling block, a media picker property on the selected MNTP node does not seem to be getting mapped correctly.
The following is my models and the typeconverter code (snipped for brevity). On the "Open Day" document type I have an MNTP property that allows one "University" node to be selected. The University document type has a media picker property on it to select the university's logo.
When I map the "Open Day" node to the model it maps all the properties of both OpenDay and University except for the UniversityLogo property. Does anyone know why this property is no being set.
p.s. I have the Umbraco Property Value Converters installed.
Hi Suzy,
OK, to clarify - the other properties of
AssociatedUniversity
are populated, except forUniversityLogo
?Just to get it out the way, I'd check for any typos in property-aliases, (sorry, it can happen) :-)
After that, the "universityLogo" property (media-picker) would contain an ID (
int
), which I'd assume that the 'Umbraco Property Value Converters' package would be picking up and converting to anIPublishedContent
.So looking from your code, everything looks to be wired up correctly.
Let me know if there is a typo ... I'm hoping I'm wrong though.
Cheers,
- Lee
Thinking about it, you might be able to remove the
UniversityTypeConverter
all together.With Ditto, if a property returns a single or list of
IPublishedContent
nodes, then it will check if the POCO's target property is a matching type - if not, then Ditto will attempt to map it to the target type.Thanks for the prompt reply.
I initially tried it without the TypeConverter and it didn't work which is why I added it.
Not sure exactly what I did as I was playing around trying to work out the problem but it seems to work now. I can only think there was some issue with the cache as I didn't change any code, just made sure all the Open Day and University pages had properties set and re-saved them.
It should work - at least that's how we wanted it to work - saves developers having to write custom TypeConverters, etc.
We've got a unit-test to support the scenario...
https://github.com/leekelleher/umbraco-ditto/blob/master/tests/Our.Umbraco.Ditto.Tests/NestedPublishedContentTests.cs
... but I guess other code/packages (real-world things) can come into play. Would be great to have this working for all.
Anyway, glad that your code is working now. Have a good weekend!
Cheers,
- Lee
is working on a reply...