A Better Razor Foreach Loop
Yesterday, during my ASP.NET MVC 3 talk at Mix 11, I wrote a useful helper method demonstrating an advanced feature of Razor, Razor Templated Delegates.
There are many situations where I want to quickly iterate through a
bunch of items in a view, and I prefer using the foreach
statement.
But sometimes, I need to also know the current index. So I wrote an
extension method to IEnumerable<T>
that accepts Razor syntax as an
argument and calls that template for each item in the enumeration.
public static class HaackHelpers {
public static HelperResult Each<TItem>(
this IEnumerable<TItem> items,
Func<IndexedItem<TItem>,
HelperResult> template) {
return new HelperResult(writer => {
int index = 0;
foreach (var item in items) {
var result = template(new IndexedItem<TItem>(index++, item));
result.WriteTo(writer);
}
});
}
}
This method calls the template for each item in the enumeration, but
instead of passing in the item itself, we wrap it in a new class,
IndexedItem<T>
.
public class IndexedItem<TModel> {
public IndexedItem(int index, TModel item) {
Index = index;
Item = item;
}
public int Index { get; private set; }
public TModel Item { get; private set; }
}
And here’s an example of its usage within a view. Notice that we pass in Razor markup as an argument to the method which gets called for each item. We have access to the direct item and the current index.
@model IEnumerable<Question>
<ol>
@Model.Each(@<li>Item @item.Index of @(Model.Count() - 1): @item.Item.Title</li>)
</ol>
If you want to try it out, I put the code in a package in my personal
NuGet feed for my code samples. Just connect NuGet to
http://nuget.haacked.com/nuget/ and
Install-Package RazorForEach
. The package installs this code as source
files in App_Code.
UPDATE: I updated the code and package to be more efficient (4/16/2011).
Comments
24 responses