A
Helper in pre-ASP.NET Core Razor is a reusable snippet of Razor syntax exposed as a method, intended for rendering HTML to the browser. ASP.NET Core offers a number of other options for creating reusable snippets of HTML -
partial pages,
taghelpers and
view components.
The difference between these approaches and a method defined in a @helper
block is that the @helper
block can be declared in a page. So,
while it may not be so reusable across an application, it's a
very useful thing if you only want to use the method in one page. It means less switching between files.
You might have a page that includes a number of unordered lists, all requiring slightly different styling. Here's how you might declare a pre-ASP.NET Core Razor helper to assist with that:
@helper RenderList(IEnumerable<string> items, string style){ <ul> @foreach(var item in items){ <li class="@style">@item</li> } </ul> }
This is called within the page using the following syntax:
@RenderList(new[]{"A","B","C"}, "pretty")
The @helper
syntax wasn't included in ASP.NET Core. Instead, in
ASP.NET Core 2.x or earlier, you could use a Templated Razor Delegate, which has
been around since Razor was first introduced. Early examples of templated Razor
delegates tend to make use of the dynamic
type, probably because
Razor itself is rooted in the Web Pages framework which made heavy use of the
dynamic
type. Therefore it is common to see templated Razor
delegate declarations like this:
Func<dynamic, object> template = @<div>@item</div>
The dynamic
parameter can represent any type. It is exposed
within the Razor template by the special parameter named item
.
Unlike the @helper
example which can be designed to accept any
number of parameters, the templated Razor delegate can only take one. If you wanted to apply this
approach to replicate the RenderList
helper defined earlier, you would create a class to
package up the list items and the string. Then you can define your delegate:
@{ class ListPackage { public string[] ListItems { get; set; } public string Style { get; set; } } Func<dynamic, object> template = @<ul> @foreach (var listItem in item.ListItems) { <li class="@item.Style">@listItem</li> } </ul>; }
While the example above shows the template being declared as a local method
(in a Razor @{ }
block), you can also declare the template in a
@functions
block. You can use the template in the content part of the page/view like this:
@template(new ListPackage { ListItems = new[] { "A","B","C" }, Style = "pretty" })
The dynamic approach offers the potential of a little flexibility, in that
you can pass any object in to the delegate, so long as it has a ListItems
property that can be enumerated, and a Style
property that can be
rendered.
You can take a more strongly typed approach by declaring your template as a
Func<[some type], IHtmlContent>
. The definition of the template
changes to the following:
@{ Func<ListPackage, IHtmlContent> template = @<ul> @foreach (var listItem in item.ListItems) { <li class="@item.Style">@listItem</li> } </ul>; }
This approach can only be employed as a local method. It will not work in a
@functions
block. You will also need to add a using
directive to reference
Microsoft.AspNetCore.Html
. The usage within the content part of the page
remains the same.
ASP.NET Core 3
A few things slipped relatively quietly into ASP.NET Core 3.0, largely
outshone by the glare of Blazor. One of those things is a more formal
replacement for the Razor helper. You can now include HTML markup in the body of
a method declared in a code block as a local method as previously, or in an @functions
block. The method should
return void,
or Task
if it requires asynchronous
processing. Here is how the list helper is written in ASP.NET Core 3:
@{ void Template(string[] listItems, string style) { <ul> foreach (var listItem in listItems) { <li class="@style">@listItem</li> } </ul> } }
Much like the old helper approach, the methods can take any number of
parameters. The syntax for calling the method in the place where the output is
to be rendered differs slightly because it returns void
, so it needs to be called in a local code block:
@{ Template(new[] { "A","B","C" }, "pretty" ); }
It should be noted that all of these methods are based on the use of Razor, so they will only work in a Razor (.cshtml) file. They are not applicable in a standard C# class file.
Summary
Most of the time, one of the solutions that involve extra files will be the best option for delivering reusable snippets of HTML. But if your reuse case doesn't extend beyond the current page, templated Razor delegates will serve you well in ASP.NET Core 2.x or lower, while ASP.NET Core sees the introduction of a much neater solution - markup within a method body.