Following on from this question foreach with index I have been trying to do something along the following lines:
Using an extension method:
public static void Each<T>(this IEnumerable<T> ie, Action<T, int> action)
{
var i = 0;
foreach (var e in ie) action(e, i++);
}
Do an iteration in my view using the index, and outputting the result of a helper method.
<div class="col-md-12">
@{
Model.Take(5).Each((item, n) =>
{
@RenderItem(item, n == 3);
});
}
</div
With the following helper
@helper RenderItem(Item item, bool special = false)
{
<p>Special rendeing for special items in here</p>
}
However the output is swallowed and not output. Is there a trick to get this to work?
Using this extension method you are not sending anything to the view. The return value of @RenderItem
is never sent to the view. Razor helpers are functions that simply return a HelperResult
but you need to send this HelperResult
to the view. When you invoke a helper using @MyHelper
this renders the HelperResult
because that is what the @
does: render something.
But, when you are doing:
Model.Take(5).Each((item, n) =>
{
@RenderItem(item, n == 3);
});
You are just calling your helper but not rendering anything to the screen (note the ending ;). In this case the @ is not the render operator, is just the "go to Razor" switch.
What probably you would like to render is the output of .Each
but you can't because Each is a void method.
I played a little with your code, just to show you these concepts. First I changed your Each
method to return an IEnumerable<HelperResult>
:
public static IEnumerable<HelperResult> Each<T>(this IEnumerable<T> ie, Func<T, HelperResult> action)
{
var i = 0;
foreach (var e in ie) yield return action(e);
}
When a method expects a HelperResult
in code (as the Func<T,HelperResult>
) you can pass a Razor expression on it, so in my view i can do:
@Enumerable.Range(1, 10).Each(i => @RenderItem(i, i == 3))
This will invoke my RenderItem
helper but the output is just the following (note that if you put a breakpoint in RenderItem the breakpoint won't hit due the lazy invocation of Linq, just add a .ToList() after the Each call to force evaluation):
WebApplication1.FOo+<Each>d__0`1[System.Int32]
(If you used .ToList() this will change to something like System.Collections.Generic.List
1[System.Web.WebPages.HelperResult]`)
This is because Razor knows how to render a HelperResult
but not a IEnumerable<HelperResult>
which is what we really have.
So... what we need to do? Yes, just iterate over the result (using a standard foreach) and displaying every result:
@{
var x = Enumerable.Range(1, 10).Each(i => @RenderItem(i, i == 3));
foreach (var ix in x)
{
@ix
}
}
That will work as expected, you now are rendering your HelperResult one by one.
Of course, this code is just to show how Razor templates work :)
Hope it helps.