MVC 3 multiple DisplayFor-Templates
Asked Answered
B

4

14

I'm trying to make a custom template for a basket item list. I need a few different templates, as I have different ways of displaying the item, depending on if it's on the webpage or in a mail. Now my problem is, that when I use the default name it works flawlessly.

@Html.DisplayFor(b => b.Items)

But when I try to add a template name, I get an expection that my templates needs to be of a list type IEnumerable and not BasketItem.

@Html.DisplayFor(i => basket.Items, "CustomerItemBaseList")

Any ideas where my mistake is, or why it's not possible are appreciated. Thanks.

Boundless answered 29/4, 2011 at 15:24 Comment(2)
You should post the code for your display template(s).Acaroid
also, what's your b.Items type? Being plural makes me think that its a collection? wouldn't your template have to be for IEnumerable(Of BasketItem) or something?Floris
E
23

Unfortunately that's a limitation of templated helpers. If you specify a template name for a collection property the template no longer applies automatically for each item of the collection. Possible workaround:

@for (int i = 0; i < Model.Items.Length; i++)
{
    @Html.DisplayFor(x => x.Items[i], "CustomerItemBaseList")
}
Emendate answered 29/4, 2011 at 17:24 Comment(2)
I was hoping for it to work the other way, but thanks, now I know that I can stop trying =)Boundless
This worked beautifully for me. Phil Haacked's article filled out the remaining details. haacked.com/archive/2008/10/23/model-binding-to-a-list.aspxCammie
D
2

That's a good idea, Darin. I'm lazy though, so I'd like to take it one step further and make an actual helper that wraps this. I also took out the lambda expression to simplify it for my case, but you can easily add that functionality back in.

    public static class DisplayTextListExtension
{
    public static MvcHtmlString DisplayForList<TModel>(this HtmlHelper<TModel> html, IEnumerable<string> model, string templateName)
    {
        var tempResult = new StringBuilder();

        foreach (var item in model)
        {
            tempResult.Append(html.DisplayFor(m => item, templateName));
        }

        return MvcHtmlString.Create(tempResult.ToString());
    }
}

Then the actual usage looks like:

                                @Html.DisplayForList(Model.Organizations, "infoBtn")
Durrett answered 1/11, 2013 at 21:35 Comment(3)
I ran into a couple of minor snags that others might want to know about: To use DisplayFor in the helper, it needs using System.Web.Mvc.Html; (and Studio won't offer it as an "option to help bind"). To see the helper from the ASP page, you need <%@ Import Namespace="YourNamespace" %>Seventy
Ah, okay Chuck. I use Resharper so if I had that issue, it would have offered to automatically fix it for me. I highly recommend it for any .NET developer. I believe there's one or two competing tools as well. Give them a try too, if you want.Durrett
@DanCsharpster Thanks, just adjusted for any IEnumerable in my answer.Impiety
I
1

I liked Dan's answer but just adjusted slightly as it can work for any IEnumerable:

using System.Collections;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace YourProject.Whatever
{
    public static class DisplayExtensions
    {
        public static MvcHtmlString DisplayForIEnumerable<TModel>(this HtmlHelper<TModel> html, IEnumerable model, string templateName)
        {
            var tempResult = new StringBuilder();

            foreach (var item in model)
            {
                var item1 = item;
                tempResult.Append(html.DisplayFor(m => item1, templateName));
            }

            return MvcHtmlString.Create(tempResult.ToString());
        }
    }
}

And of course:

@Html.DisplayForIEnumerable(Model.Organizations, "NameOfYourDisplayTemplate")
Impiety answered 10/5, 2015 at 3:5 Comment(0)
A
1

I suggest another solution useful even more with lists of heterogeneous objects (i.e. BasketItem subclasses), using the additionalViewData parameter of the DisplayFor method, like:

@DisplayFor(b=>b.Items, new { layout="row" })

in this way the helper works fine with IEnumerable<T>, calling for each item (subclass of T) the relative DisplayTemplate, passing it the additionalViewData values in the ViewData dictionary.

The template could so output different code for different layout values.

In the example above the template named View\Shared\DisplayTemplates\BasketItem (or the name of the subclass) should be like this:

@model MyProject.BasketItem // or BasketItem subclass
@{ 
    string layout = ViewData["layout"] as string ?? "default";

    switch(layout)
    {
        case "row":
            <div class="row">
            ...
            </div>
        break;
        // other layouts
        ...
        default: // strongly recommended a default case
            <div class="default-view>
            ...
            </div>
        break;
    }
}

It is strongly recommended to provide always a default code.

I hope this suggestion could help.

Accouter answered 4/2, 2016 at 16:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.