3

I've got a view that iterates a collection and calls DisplayFor() for each element in the collection.

I need to manually iterate (as opposed to passing the collection to DisplayFor) in order to tell the template if a break in the list should be drawn. The items in the list will only be of 2 types, ordered by them, so I only need to show this break once.

My template is found and called correctly.
I can see the HTML it generates correctly, ie: DisplayFor().ToHtmlString()
I can set this HTML as a scoped variable, ie: var html = DisplayFor().ToHtmlString() ..
But even Html.Raw(html) does not render it in the browser - the HTML has simply vanished.

What's going on?

var renderBreakInList = Model.Items.Any(x => x.IsSomeType);
foreach(var item in Model.Items)
{
    var renderBreak = renderBreakInList && item.IsOtherType;
    Html.DisplayFor(x => item, new { renderBreak = renderBreak });

    if (renderBreak)
    {
        renderBreakInList = false;
    }
}
JoeBrockhaus
  • 2,745
  • 2
  • 40
  • 64
  • 2
    You sample code looks strange... `DisplayFor` should be `Html.DisplayFor`... Can you post your actual code what is not working? By the way the call `@Html.DisplayFor(x => item, new { renderBreak = renderBreak });` should work... – nemesv Jul 01 '13 at 19:27
  • yah you're right it has `Html.` just forgot to add that in transposing this – JoeBrockhaus Jul 01 '13 at 20:17
  • Still the at sign `@` is missing... Have you tried with `@Html.DisplayFor(x => item, new { renderBreak = renderBreak });`? – nemesv Jul 01 '13 at 20:21

2 Answers2

4

The Html.DisplayFor method in itself does not render anything to the response just returns the generated HTML as a MvcHtmlString.

In order to actually write the rendered HTML to the response you need to tell this to Razor with using the @ sign:

@Html.DisplayFor(x => item, new { renderBreak = renderBreak })

So your whole code should look like this:

@{
    var renderBreakInList = Model.Items.Any(x => x.IsSomeType);
    foreach(var item in Model.Items)
    {
        var renderBreak = renderBreakInList && item.IsOtherType;
        @Html.DisplayFor(x => item, new { renderBreak = renderBreak })

        if (renderBreak)
        {
            renderBreakInList = false;
        }
    }
}

Or you can use the WebPageBase.Write method (which gets called under the hood when using the @ sign):

Write(Html.DisplayFor(x => item, new { renderBreak = renderBreak }));
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • oh derp. didn't realize you had already answered this. jinx & thanks! took me awhile to figure out that ***sometimes*** you need to prepend stuff with @ while in a code block, and other times you can't - I just got in the habit of not doing so to avoid that "you don't need the @ inside a code block" error. I got it to work by assuming it would give me that and placing a `display: none;` span in front of it *then* added the @. Silly me.. You win the answer, even though while it makes my template not as robust, I prefer the cleaner look of the code that just passes the itemID to break on :P – JoeBrockhaus Jul 01 '13 at 20:38
0

finally figured this out after trying a lot of different things and reworking how I tell the Template to draw the break.

Rather than send a bool which I'd prefer to make the template more robust (if the order changes), I'm passing in the ID of the item that should draw the break.

@{ 
    var breakItem = Model.Items.FirstOrDefault(x => renderBreakInList && x.IsSomeType);
    var breakID = breakItem == null ? (long?)null : (long)breakItem.ID;
}

@Html.DisplayFor(x => x.Items, new { breakID = breakID })    

Also like nemesv pointed out, Html.DisplayFor() needs to be prepended with @. I got out of the habit of doing this inside code blocks because I would always get the 'once inside a code block you don't need @` error..

JoeBrockhaus
  • 2,745
  • 2
  • 40
  • 64