1

(Apologies for title .. was hard to nail down)

I have a situation where a controller will build a List which will contain a set of ViewModels derived from the abstract base class. I then use display templates to render the content appropriately, but when I navigate to the url it appears to rendering the whole thing twice. Here is my code

public abstract class AbstractItemViewModel
{
}

public class TypeAViewModel : AbstractItemViewModel
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
}

public class TypeBViewModel : AbstractItemViewModel
{
    public string Title { get; set; }
    public List<string> Items { get; set; }
}

Then my controller builds a test case as such.

public class HomeController : BaseController
{
    // GET: Home
    public ActionResult Index()
    {
        List<AbstractItemViewModel> items = new List<AbstractItemViewModel>();

        items.Add(new TypeAViewModel() { ID = 1, Title = "Test A", Body = "This is some body content." });
        items.Add(new TypeBViewModel() { Title = "Test B", Items = new List<string>() { "Line 1", "Line 2", "Line 3" } });
        return View(items);
    }
}

... and for completeness here is the base controller class (nothing special) ..

public class BaseController : Controller
{

}

.. and here is the View ...

@using xxx.ViewModels
@model List<AbstractItemViewModel>

@{
    ViewBag.Title = "Home";
}


<h1>@Model.Count()</h1>
<div class="container">
    <div class="row">
        @foreach (AbstractItemViewModel item in Model)
        {
            <div class="col-xs-12">
                @Html.DisplayForModel(item)
            </div>
            <p>space</p>
        }
    </div>
</div>

.. and 2 display templates TypeAViewModel.cshtml

@using xxx.ViewModels
@model TypeAViewModel

<h2>@Model.Title (@Model.ID)</h2>
<p>@Model.Body</p>

.. and ... TypeBViewModel.cshtml

@using xxx.ViewModels
@model TypeBViewModel

<h2>@Model.Title</h2>
<ul>
    @foreach (string s in Model.Items)
    {
        <li>@s</li>
    }
</ul>

As output I get.

2

Test A (1) This is some body content.

Test B Line 1 Line 2 Line 3 space

Test A (1) This is some body content.

Test B Line 1 Line 2 Line 3 space

As you can see it appears to be rendering the whole thing twice. I've placed a breakpoint and stepped through the Index View at definitely doesn't repeat the loop as such. Anybody see what I'm missing?

Simon Rigby
  • 1,786
  • 4
  • 17
  • 28

1 Answers1

1

You should use DisplayFor Html helper instead of DisplayForModel, since DisplayForModel sends your entire model (in this case complete list of passed in models - descendants of AbstractItemViewModel; item is actually only additional data to the model passed in) to the views. Actually for every item in foreach, complete model gets passed to your view; e.g. if you would have 3 models inside your list, they would be rendered 3 times each (if 4, then 4 times, etc.).

So for this to work use this helper: @Html.DisplayFor(model => item) inside foreach statement instead of DisplayForModel.

Jure
  • 1,156
  • 5
  • 15