umco / umbraco-ditto

Ditto - the friendly view-model mapper for Umbraco
http://our.umbraco.org/projects/developer-tools/ditto
MIT License
79 stars 33 forks source link

Cannot augment Value to process. #175

Closed JimBobSquarePants closed 8 years ago

JimBobSquarePants commented 8 years ago

With pre 0.9.0 versions of Ditto I could use a TypeConverter to easily process NuPicker values.

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
    Picker picker = value as Picker;
    if (picker != null)
    {
        if (picker.PickedKeys.Any())
        {
            return base.ConvertFrom(context, culture, picker.PickedKeys);
        }

        return null;
    }

    return base.ConvertFrom(context, culture, value);
}

This was possible as I was able to pass a different value to the ConvertFrom method.

In 0.9.0 the Value property setter is internal as is the ProcessValue(value, context) method. This looks like it means I would have to implement all the code contained within UltimatePickerAttribute in order to implement that processor.

mattbrailsford commented 8 years ago

Hey Jim,

You'll probably want to make a NuPicker attribute like this:

public class NuPickerAttribute : DittoProcessorAttribute
{
    public override object ProcessValue()
    {
        var picker = this.Value as Picker;
        if (picker != null && picker.PickedKeys.Any())
        {
            return picker.PickedKeys;
        }
        return this.Value;
    }
}

Then you'll want to chain them on your property like:

[UmbracoProperty(Order = 0)]
[NuPicker(Order = 1)]
[UltimatePicker(Order = 2)]
public IPublishedContent MyPicker { get; set; }

The key thing to remember is that the output of a processor, is the input for the next processor

mattbrailsford commented 8 years ago

If you find you are defining that on your properties a lot, you could also wrap it in a multi processor for convenience:

public class MyNuPickerAttribute : DittoMultiProcessorAttribute
{
    public MyNuPickerAttribute ()
        : base(new DittoProcessorAttribute[]
        {
            new UmbracoPropertyAttribute(),
            new NuPickerAttribute(),
            new UltimatePickerAttribute()
        })
    { }
}

then your property would be like:

[MyNuPicker]
public IPublishedContent MyPicker { get; set; }
JimBobSquarePants commented 8 years ago

That second one's the bad boy I'm after. Ace!

naepalm commented 7 years ago

I don't suppose it would be possible to add this to the documentation? I went through hell today trying to figure out the answer to my problem, haha. And here it was, a beautiful example!

leekelleher commented 7 years ago

@naepalm We'll add it for sure 👍

Getting all the docs up to date is planned for our v1.0 rollup release - in terms of when that will happen, we're currently neck-deep in client work, (aren't we all, right? 😃) - but yes, it's still very much on our todo list.

JimBobSquarePants commented 7 years ago

@leekelleher @naepalm

Agreed... Though I would say we're pretty much feature complete now. (Once my PR is merged) so we should concentrate on (code cleanup/performance) and testing a few final things in preparation for that release. (What happens when someone uses List\<T> instead of IEnumerable\<T> against the UmbracoPicker?)

Oh and here's up-to-date NuPicker examples

Picker

/// <summary>
/// Provides a unified way of converting the <see cref="Picker"/> type to the given <see cref="Type"/>
/// </summary>
public class NuPickerAttribute : DittoMultiProcessorAttribute
{
    /// <summary>
    /// Initializes a new instance of the <see cref="NuPickerAttribute"/> class.
    /// </summary>
    public NuPickerAttribute()
        : base(new DittoProcessorAttribute[] { new NuPickerConverterAttribute(), new UmbracoPickerAttribute() })
    {
    }

    /// <summary>
    /// Returns the correct value to pass to the <see cref="UmbracoPickerAttribute"/>
    /// </summary>
    private class NuPickerConverterAttribute : DittoProcessorAttribute
    {
        /// <inheritdoc/>
        public override object ProcessValue()
        {
            Picker picker = this.Value as Picker;
            if (picker != null && picker.PickedKeys.Any())
            {
                return picker.PickedKeys;
            }

            return this.Value;
        }
    }
}

EnumPicker

/// <summary>
/// Provides a unified way of converting the <see cref="Picker"/> type to the given enum. />
/// </summary>
public class NuPickerEnumAttribute : DittoMultiProcessorAttribute
{
    /// <summary>
    /// Initializes a new instance of the <see cref="NuPickerEnumAttribute"/> class.
    /// </summary>
    public NuPickerEnumAttribute()
        : base(new DittoProcessorAttribute[] { new NuPickerEnumConverterAttribute(), new EnumAttribute() })
    {
    }

    /// <summary>
    /// Returns the correct value to pass to the <see cref="EnumAttribute"/>
    /// </summary>
    private class NuPickerEnumConverterAttribute : DittoProcessorAttribute
    {
        /// <inheritdoc/>
        public override object ProcessValue()
        {
            Picker picker = this.Value as Picker;
            return picker != null ? picker.SavedValue : this.Value;
        }
    }
}

PROTIP: Ditto automatically adds the UmbracoProperty attribute so you never really have to.

naepalm commented 7 years ago

@JimBobSquarePants Thank you SO MUCH for including those. Until the docs get updated, I'll just bookmark this for myself as a reminder ;)

JimBobSquarePants commented 7 years ago

No worries, always happy to help 😄 Gonna try to get stuck into the docs soon.

nhaberl commented 7 years ago

Really excited about this release [docs] :-)