3

My display template "PlanAlternate":

@model MyPlan

<div class="span4 compare-column compare-column1">

<div class="compare-row-plan">
    <h3>@Model.Plan.Name</h3>
</div>

@foreach (var benefit in Model.Plan.Benefits)
{
    <div class="compare-row-benefit-description">
        @benefit.Description
    </div>
}

<div class="compare-row-submit">
    <input class="btn" type="submit" value="Apply Now" />
</div>
</div>

My View that uses the display template:

@model IEnumerable<MyPlan>

<div class="span9 compare-data-wrapper">
   @Html.DisplayForModel("PlanAlternate")
</div>

This throws an exception:

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[MyPlan]', but this dictionary requires a model item of type 'MyPlan'.

HOWEVER, If I rename the display template from PlanAlternate.cshtml to MyPlan.cshtml and use

@Html.DisplayForModel()

Everything works just fine. Can you help me understand why this is happening?

Thanks!

EDIT:

If I do exactly as described above where the display template file name matches the view model name, but I pass the view name ("MyPlan") to DisplayForModel, it still throws an exception!! Calling DisplayForModel with no arguments works. This seems very, very weird to me. Almost like a bug but I'm sure it's just something I don't understand.

tereško
  • 58,060
  • 25
  • 98
  • 150
Barry
  • 101
  • 6

2 Answers2

3

The DisplayForModel() (and the DisplayFor(m => m)) method internally uses the internal static methods in the TemplateHelpers class. The code is quite complex and I'll leave it to your to explore the source code in more detail, but to summarize what happens:

When you do not pass a templateName value, the helper basically says

Find me the view name name that matches my model name and generate the html based on that view for each item in the my collection

But when you do pass a templateName value, the helper basically says

Find me view that matches templateName and pass my collection to it and generate the html based on that view

which results in your exception, because its passing a model of IEnumerable<MyPlan> to a view that expects a model which is MyPlan.

This works the same as when using the [TemplateHint] attribute - i.e it passes the model to the view, so it the model is IEnumerable<T>, the the view must also be @model IEnumerable<T>.

So it is by design (not a bug) and it does give you a lot of flexibility (for example to allow you to multiple templates for the same model), although the behavior is probably not as well documented as it should be.

0

Your template expects a model of a single MyPlan instance where as your view has a list of MyPlan objects. Because you're not providing any object to DisplayForModel it just passes the model from the view. To do what you want, you'll have to wrap the DisplayForModel in a for loop and pass an instance of MyPlan from the IEnumerable on each iteration of the loop, i.e.

foreach(var myPlan in Model)
{
    @Html.DisplayForModel("PlanAlternate", myPlan)
}

I haven't checked the syntax but that looks right to me.

DoctorMick
  • 6,703
  • 28
  • 26
  • 1
    Why would it be that simply renaming the display template to match the model would fix the issue? – Barry Jun 25 '13 at 15:56
  • But why does it work with `@Html.DisplayForModel()` if OP changes name of display template file name but not `@Html.DisplayForModel("PlanAlternate")`? – Martin Dawson Mar 09 '16 at 18:34