1

I am aware that complex types are not usually rendered when using EditorForModel but I am using a custom object template that does not do the check and calls Html.Editor for every property including complex types.

Unfortunately, whilst I can see the correct TemplateHint value for the property within the object template, the Editor call doesn't seem to use it and the built in collection template is used instead.

My object template is basically this:

@foreach (var property in ViewData.ModelMetadata.Properties.Where(x => x.ShowForEdit))
{
  @Html.Editor(property.PropertyName)
}

If I force the use of the template by passing the name to the Editor call then the ModelMetadata is empty in the template.

Is this a bug / are there any workarounds?

Some more info:

So my view model contains the following:

[ACustomAttribute("Something")]
public IEnumerable<int> SelectedOptions { get; set; }

The attribute implements IMetadataAware and adds some stuff to the AdditionalValues Collection of the ModelMetadata as well as setting the TemplateHint. I can read this data from the object template but not from my custom template.

Paul Hiles
  • 9,558
  • 7
  • 51
  • 76

1 Answers1

2
@foreach (var property in ViewData.ModelMetadata.Properties.Where(x => x.ShowForEdit))
{
    if (!string.IsNullOrEmpty(property.TemplateHint))
    {
        @Html.Editor(property.PropertyName, property.TemplateHint)
    }
    else
    {
        @Html.Editor(property.PropertyName)
    }
}

But please note that if you don't rely on the established conventions for resolving templates for complex collection types (a.k.a ~/Views/Shared/EditorTemplates/NameOfTheTypeOfCollectionElements.cshtml) and have used an UIHint on your collection property:

[UIHint("FooBar")]
public IEnumerable<FooViewModel> Foos { get; set; }

then the ~/Views/Shared/EditorTemplates/FooBar.cshtml editor template must be strongly typed to IEnumerable<FooViewModel> and not FooViewModel. So be careful, if this is your case, it's up to you to loop inside this custom template if you want to get to individual items of the collection. It will no longer be ASP.NET MVC that will automatically loop for you and invoke the editor template for each element.


UPDATE:

Still can't repro your issue.

Custom attribute:

public class ACustomAttribute : Attribute, IMetadataAware
{
    private readonly string _templateHint;
    public ACustomAttribute(string templateHint)
    {
        _templateHint = templateHint;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["foo"] = "bar";
        metadata.TemplateHint = _templateHint;
    }
}

Model:

public class MyViewModel
{
    [ACustom("Something")]
    public IEnumerable<int> Foos { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Foos = Enumerable.Range(1, 5)
        };
        return View(model);
    }
}

View (~/Views/Home/Index.cshtml):

@model MyViewModel

@using (Html.BeginForm())
{
    @Html.EditorForModel()
}

Editor template for the object type (~/Views/Shared/EditorTemplates/Object.cshtml):

@foreach (var property in ViewData.ModelMetadata.Properties.Where(x => x.ShowForEdit))
{
    if (!string.IsNullOrEmpty(property.TemplateHint))
    {
        @Html.Editor(property.PropertyName, property.TemplateHint)
    }
    else
    {
        @Html.Editor(property.PropertyName)
    }
}

Custom editor template (~/Views/Shared/EditorTemplates/Something.cshtml):

@model IEnumerable<int>

<h3>
    @ViewData.ModelMetadata.AdditionalValues["foo"]
</h3>

@foreach (var item in Model)
{
    <div>
        @item
    </div>
}

Result:

enter image description here

So as you can see the additional metadata we added is shown in the template.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks. As I mentioned though, if I pass the template name through then whilst it does use the correct template, the modelmetadata is not properly built up. I have items in the AdditionalValues collection for the property that I can see from the object template but they are not passed through. Any ideas? – Paul Hiles Aug 10 '12 at 12:57
  • I don't understand on which property you have additional metadata. Could you show some code? – Darin Dimitrov Aug 10 '12 at 13:01
  • The metadata is on the collection property. It is part of an elaborate templating mechanism so difficult with the code but hopefully what I added to the question will help. – Paul Hiles Aug 10 '12 at 13:41
  • Is the updated question enough to go on or do you want some more code? Thx – Paul Hiles Aug 14 '12 at 08:15
  • @TheFlowerGuy, I still cannot reproduce your issue. I have updated my answer. – Darin Dimitrov Aug 14 '12 at 08:57
  • Thanks. Was me being an idiot. Was playing around and had added an item in ViewData with the same name as the property in my strongly typed viewmodel. When doing Html.Editor, it looks as though the ViewData 'property' was taking priority. Removed that and now working fine. Still do not understand why I need to explicitly need to tell it to use the TemplateHint for collections. Would expect it to be automatic if I have applied it to the collection property itself. – Paul Hiles Aug 14 '12 at 09:52
  • BTW, been waiting for an MVC expert like yourself for ages on this one :-) http://stackoverflow.com/questions/10013095/capture-wrapped-content-in-beginform-style-disposable-html-helper – Paul Hiles Aug 14 '12 at 10:28