Domanda

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?

È stato utile?

Soluzione

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.List1[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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top