Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't RazorViewEngine pick up my DisplayTemplate?

Application is an MVC3 application with RAZOR view engine.

Controller being used here is TestController. I am using Nested Views.

Base view(Item Listing) goes like this,

//Listing.cshtml
@model ItemsList 
@for (int i = 0; i < Model.Items.Count(); i++)
    {
        @Html.DisplayFor(x => x.Items[i], new { RowPosition = i})
}

here is the template for Item

//Item.cshtml
@model Item
@Html.DisplayFor(x=>x.HeaderText)
@Html.DisplayFor(x=>x, "ItemDetails")

And here is the view for Item Details

//ItemDetails.cshtml
@model Item
@Html.DisplayFor(x=>x.Description)

So, I am trying to pass forward model from ITEM template to ITEMDETAILS template. ItemDetails.cshtml is placed under "Views\Test\DisplayTemplates". Infact i have tried placing it under folders "Views\Shared" as well as "Views\Shared\DisplayTemplates". But View engine seems just not to picking it up.

However Microsoft documentation here states that view engine does look in Controller\DisplayTemplates folder to fetch the VIEW using TemplateName used.

like image 834
EagerToLearn Avatar asked May 03 '12 20:05

EagerToLearn


2 Answers

This appears to be the intended behaviour for Display/EditorTemplates, presumably to prevent accidental infinite recursion in custom display templates, such as doing (in Item.cshtml):

@model Item
@Html.DisplayFor(x => x)

...which would infinitely display the Item.cshtml DisplayTemplate.

Obviously in your example you're passing the item/model to a different template, so it wouldn't cause infinite recursion. However, it still seems to get caught by the same safe-guard in the framework. Not really sure if it would be classified as a 'bug' or just 'by design'?

This is the check in the DisplayFor/TemplateFor helper:

// Normally this shouldn't happen, unless someone writes their own custom Object templates which 
// don't check to make sure that the object hasn't already been displayed 
object visitedObjectsKey = metadata.Model ?? metadata.RealModelType;
if (html.ViewDataContainer.ViewData.TemplateInfo.VisitedObjects.Contains(visitedObjectsKey)) {    // DDB #224750 
    return String.Empty;
}

ViewData.TemplateInfo.VisitedObjects stores the visited objects/models for parent templates. When you run:

@Html.DisplayFor(x => x.Items[i], new { RowPosition = i})

It renders your Item.cshtml DisplayTemplate and adds the item/model to VisitedObjects. This means when Item.cshtml tries to display another child template with the same item/model:

@Html.DisplayFor(x => x, "ItemDetails")

The item/model is already in VisitedObjects, so the if statement above returns true and instead of rendering ItemDetails.cshtml it just silently returns/renders an empty string.

like image 58
pjumble Avatar answered Nov 02 '22 23:11

pjumble


try using @Html.RenderPartial("ItemDetails", item)

like image 27
Bob The Janitor Avatar answered Nov 03 '22 00:11

Bob The Janitor