Rendering a ContentField with the default renderer

Topics: Customizing Orchard
Mar 26, 2013 at 4:50 AM
I'm trying to customise the rendering for a content type I've created called "Banner Image". This is my View file at the bottom of this post (Content-BannerImage.Summary.cshtml).

I've used the 'Markdown' field for my title field. However... I can't figure out how to access the Markdown rendering from here. I've tried just getting the ContentField object and calling @Display() but it doesn't work, it just puts out "Orchard.Core.Common.Fields.TextField". Could someone please tell me how I can render this field using the Markdown module's renderer?
@{
    var imageUrl = ((ContentItem)Model.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "Image").First().Storage.Get<string>(null);

    var titleField = ((ContentItem)Model.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "Title").First();
}

@if (HasText(imageUrl))
{
    
    <div class="banner-image">
        <img src="@Href(imageUrl)" width="698" />
        <div class="overlay">
            <h2>@Display(titleField)</h2>
        </div>
    </div>
}
Thanks!
Coordinator
Mar 26, 2013 at 7:13 AM
There is no markdown field that I know of. There is a text field that you can configure for Markdown. You can access its value through the Value property.
Mar 26, 2013 at 10:51 PM
Yes, sorry it's a text field configured as Markdown. However if I try to access the value through the Value property:
var titleValue = ((ContentItem)Model.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "Title").First().Value;
This doesn't compile/crashes.

I can access it like follows:
string titleValue = ((ContentItem)Model.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "Title").First().Storage.Get<string>(null);
However then I am just left with a string and no way to render as HTML. I don't want to have to bring Markdown libs into the Theme just to render it... I just want to go hey field, render yourself.

Another option is I find the shape in the model and then call @Display() on the shape... but then I get all the extra stuff rendered with it like the fieldname that I don't want. Then I would have to create another view template to override the rendering of that, and well... things are just getting a little out of hand for doing a simple rotating banner!
Coordinator
Mar 27, 2013 at 8:14 AM
The Value property of the shape should already be translated into HTML, so Model.Value from a text field template override should do the trick.
Mar 27, 2013 at 10:32 PM
Thanks Bertrand. Here's my solution (based on this tutorial):

I modified the function from the tutorial so that it looks in alternates instead of just the type, this way I can specify exactly the field I'm looking for. I must say... this feels loose and hacky, but I don't know a better way.
public static dynamic Find(IShape model, string name)
{
    var zones = new Dictionary<string, object>();
    ((IClayBehaviorProvider)model).Behavior
        .GetMembers(_nullFunc, model, zones);
    foreach (var key in zones.Keys
        .Where(key => !key.StartsWith("_")))
    {

        var zone = zones[key] as IShape;
        if (zone == null ||
            zone.Metadata.Type != "ContentZone") continue;
        foreach (IShape shape in ((dynamic)zone).Items)
        {
            if (shape.Metadata.Alternates.Any(a => Regex.IsMatch(a, "^" + name, RegexOptions.IgnoreCase))) {
                return shape;    
            }                    
        }
    }
    return null;
}
Then in my View:
dynamic foundShape = ShapeHelper.Find((Shape)Model, "Fields_Common_Text__Title");
var titleField = foundShape.Value;
Developer
Mar 27, 2013 at 10:41 PM
Edited Mar 27, 2013 at 10:42 PM
You don't need to resort to finding shapes like that if you simply render a local zone, e.g. @Display(Model.TitleZone), and use Placement.info to place the text field into that TitleZone (assuming that you are customizing a content item template, which you seem to be doing).
Mar 27, 2013 at 11:14 PM
Thanks skywalker, I didn't know about local zones. I guess one annoyance with doing it that way is I have to override the text field template (because I don't want to display the field name and surrounding div). So if I have several fields on a content item then I will potentially need to customise the text field template for each one..
Developer
Mar 27, 2013 at 11:20 PM
Edited Mar 27, 2013 at 11:20 PM
Perhaps, but only if you intend to do customizations for each field. If the only thing to be changed for a text field is to remove the display name and surrounding div, you only have to do that once in your template override.

Alternatively, you could file a bug requesting that we add a lazy field property to the TextField and BodyPart that returns the transformed value. Personally I think it makes sense, and actually needed this today (but solved it differently by adding such property to my custom part). It might be something worth considering.