Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Chris Wilson 100 posts 377 karma points
    Sep 10, 2014 @ 16:55
    Chris Wilson
    0

    UmbracoMapper & MultiNodeTreePicker

    Introduction

    Hello there,

    Firstly, I love the package, you've saved me a great deal of mapping stress so far! I have run into a problem however, so I'm hoping you can help as I'm on a limited schedule!

    The platform I'm building on is 7.14.

    The problem

    So essentially what I've done is created a new Data Type called MultiNodeContentPicker, which behind the scenes is a MultiNodeTreePicker which can only select from the Content tree.

    The model for the page holding this property is simple, as follows:

        public class GlobalModel
        {
            public string trademark { get; set; }
            public IEnumerable<IPublishedContent> footerNavigation { get; set; }
        }
    

    Now, when I map the page which uses a property of this type to the above model using UmbracoMapper, the trademark populates correctly but the footerNavigation returns null. If I change the type of footerNavigation in the model to a string I receive a string representation of the Value member, which is something along the lines of:

    System.Linq.Enumerable.WhereEnumerableIterator<IPublishedContent>
    

    I'm assuming that receiving this rather than a CSV is a consequence of the Umbraco Core Property Editor Converters, as the DataValue member contains the CSV you'd expect. I actually require a collection of IPublishedContent as I'm creating a navigation bar, so this would actually be rather handy if it worked, however as the mapping process returns null I'm assuming UmbracoMapper doesn't know how to map the type.

    Where I've gotten to

    To attempt to resolve this I'm adapting the procedure from this page to provide a mapping dictionary for MultiNodeContentPicker: http://web-matters.blogspot.co.uk/2014/08/using-umbraco-mapper-with-archetype_8.html

    The ActionResult producing the Model is this:

    public ActionResult RenderFooter(RenderModel model)
        {
            var viewModel = new StandardNavigationModel();
            var globalModel = GetPopulatedGlobalModel();
    
            if (globalModel != null)
            {
                Mapper.MapCollection(globalModel.footerNavigation, viewModel.MenuItems); 
            }
    
            return PartialView("_NavigationFooter", viewModel);
        }
    
        public GlobalModel GetPopulatedGlobalModel()
        {
            var globalNode = Umbraco.TypedContent(Constants.Global.GlobalNodeID); // globalNode.footerNavigation == System.Linq.Enumerable.WhereEnumerableIterator<IPublishedContent>
            var globalModel = new GlobalModel(); 
            Mapper.Map(globalNode, globalModel); // footerNavigation == null
    
            return globalModel;
        }
    

    As I've got a Base Controller which exposes a Mapper for inheritance purposes I've added this code to the base constructor:

    Mapper.AddCustomMapping(
                typeof (IEnumerable<IPublishedContent>).FullName,
                TypeMapper.MultiNodeContentPickerMapper);
    

    The mapping method this calls is this:

    public static IEnumerable<IPublishedContent> MultiNodeContentPickerMapper(
            IUmbracoMapper mapper,
            IPublishedContent contentToMapFrom,
            string propertyName,
            bool recursive)
        {
            var result = new List<IPublishedContent>();
    
            var multiNodePickerModel = contentToMapFrom.GetPropertyValue<IEnumerable<IPublishedContent>>(propertyName, recursive, null);
            if (multiNodePickerModel == null) return result;
    
            var MNTPasDictionary = multiNodePickerModel
                .Select(item => item.Properties.ToDictionary(
                    m => m.PropertyTypeAlias,
                    n => n.Value,
                    StringComparer.InvariantCultureIgnoreCase))
                    .ToList();
    
            mapper.MapCollection(MNTPasDictionary, result);
    
            return result;
        }
    

    However I'm getting a compiler error on the MapCollection call with the following message:

    The type 'Umbraco.Core.Models.IPublishedContent' must have a public parameterless constructor in order to use it as a parameter 'T' in the generic method 'IUmbracoMapper Zone.UmbracoMapper.IUmbracoMapper.MapCollection<T>(IEnumberable<Dictionary<string,object>>, IList<T>, Dictionary<string,PropertyMapping>, bool, string, string)'
    

    As IPublishedContent is so integral to the whole purpose of UmbracoMapper I'm assuming I've gone about this all wrong here, but as this is my first time delving this deeply into type mapping AND my first use of MultiNodeTreePicker I'm a bit stumped.

    The plea

    Please tell me if I've gone about this all wrong and there's a simple method of achieving this, however I've scoured the web, through forums and blogs and I can't find an explanation of exactly how you should go about this - this is why I'm trying to adapt methods that use MultiNodeTreePicker inside an Archetype.

    If this isn't possible, I'd appreciate it if you could let me know how to get UmbracoMapper to map the DataValue member instead. It would make the consuming code a bit messier but still far less messy and more automated than the way I'm currently achieving it.

    Any help you can offer would be outstanding!

    Kind Regards,

    Chris.

  • Andy Butland 422 posts 2333 karma points MVP 4x hq c-trib
    Sep 12, 2014 @ 22:43
    Andy Butland
    0

    Hi Chris

    Firstly sorry for the delay in responding - have been away on holiday for the last week.  Hope you've got somewhere with this in the meantime.

    I think the first thing I'd say is that possibly you are trying to do something the mapper package wasn't really intended to do.  We don't use it for mapping to fields of type IEnumerable<IPublishedContent> - in fact the whole point really was to avoid this type of thing, so we can use proper strongly typed view models in our views.

    In your case I'd probably be considering creating something like a FooterItem type, containing at least Name and Url properties, and use the package to map to an IEnumerable<FooterItem> on your GlobalModel via the MapCollection method.

    Does that make sense?

    Andy

     

Please Sign in or register to post replies

Write your reply to:

Draft