ChevCast
ChevCast

Reputation: 59173

ASP.NET MVC 3 Display Template problem

First I have a Display Template in Views > Blog > DisplayTemplates:

@model Domain.Model.BlogPost
<div class="blogSummary">
    @Html.ActionLink(Model.Title, "post", new { Id = Model.PostID }, new { @class = "titleLink" })
    <div class="subInfo">
        Posted by @Model.Author on @Model.PostedDate.ToString("D") | @Model.PostComments.Count
        Comment(s)</div>
    <br />
    <div class="summaryText">
        @Html.Markdown(Model.Body.Length > 300 ? Model.Body.Remove(0, 300) + "..." : Model.Body)
        @Html.ActionLink("(view more)", "post", new { Id = Model.PostID })
    </div>
</div>

Then I have a view:

@model IEnumerable<Domain.Model.BlogPost>
@{
    ViewBag.Title = "Index";
}
@Html.DisplayFor(x => x, "BlogPostSummary")

However, I am getting an error on the DisplayFor():

The model item passed into the dictionary is of type 'System.Data.Objects.ObjectSet`1[Domain.Model.BlogPost]', but this dictionary requires a model item of type 'Domain.Model.BlogPost'.

I'm having a bit of trouble understanding what's going on. What is the best way to use the display template?

Upvotes: 2

Views: 4200

Answers (5)

Necro'ing sorry: Just adding an answer since none these use the automatic id that the requester was asking about. The issue is that you're calling the DisplayTemplate helper with a named template. For some unknown reason, ASP .Net and ASP .Net Core disable the automatic name and id handling when a template name is specified. You should rename your displaytemplate to /Views/Shared/DisplayTemplates/BlogPost.cshtml and remove BlogPostSummary from your callsite.

So:

Template /Views/Shared/DisplayTemplates/BlogPost.cshtml

@model Domain.Model.BlogPost
<div class="blogSummary">
    @Html.ActionLink(Model.Title, "post", new { Id = Model.PostID }, new { @class = "titleLink" })
    <div class="subInfo">
        Posted by @Model.Author on @Model.PostedDate.ToString("D") | @Model.PostComments.Count
        Comment(s)</div>
    <br />
    <div class="summaryText">
        @Html.Markdown(Model.Body.Length > 300 ? Model.Body.Remove(0, 300) + "..." : Model.Body)
        @Html.ActionLink("(view more)", "post", new { Id = Model.PostID })
    </div>
</div>

and wherever you're trying to reference it:

@Html.DisplayFor(x => x)

Upvotes: 0

Ryan Lundy
Ryan Lundy

Reputation: 210090

I've been working on the same problem, and I think I have a better solution for you. You can loop through the collection and still get this to work:

@foreach(var blogPost in Model)
{
    Html.DisplayFor(x => blogPost, "BlogPostSummary")
}

Using a variable on the left side of a lambda expression doesn't necessarily require you to also use it on the right side. DisplayFor expects an Expression<Func<TModel, TValue>>. TModel is the type of your model, such as IEnumerable<BlogPost>, but TValue can be anything you like. So as long as you have a reference to the current item in your collection, as in the foreach above, you can ignore the left side of the lambda expression.

(But you do have to have the TModel. Html.DisplayFor(() => blogPost, "BlogPostSummary") doesn't work.)

Upvotes: 2

kim3er
kim3er

Reputation: 6466

The problem is you're trying to pass in a collection of BlogPosts, when a single BlogPost is expected.

Try:

@Html.DisplayFor(x => x[0], "BlogPostSummary")

Or something like:

@foreach (var post in Model)
    @Html.Display(post)

Note: I'm not convinced the second example is using Html.Display correctly. My suspicion is that the code would be better served using a partial instead of a display template.

UIHint

By default, ASP.NET MVC relies on naming conventions to link BlogPost the class to BlogPost.cshtml the display template. If however, you would like to use CustomBlogPost.cshtml as your display template, you can do so by applying the UIHint attribute.

public class DomainModel {
    [UIHint("CustomBlogPost")]
    public BlogPost Post { get; set; }
} 

Upvotes: 4

Yngve B-Nilsen
Yngve B-Nilsen

Reputation: 9676

Basically it's because doing an @Html.DisplayFor() implicitly tells MVC that the Model to pass on is the Model of the passing View, i.e. IEnumerable<Domain.Model.BlogPost>

What you need to do is enumerate over the Model, and display each of them:

@foreach(var blogPost in Model){

    Html.RenderPartial("BlogPostSummary", blogPost)

}

Make sure the BlogPostSummary is in the correct folder, and you should be good to go!

Upvotes: 0

jessegavin
jessegavin

Reputation: 75640

Try this maybe

@Html.DisplayFor(x => x.First(), "BlogPostSummary")

Upvotes: 2

Related Questions