Create Custom HTML Helper in ASP.Net Core
I want to create my own custom HTML Helper like the ones used in ASP.NET MVC, but I haven't been able to find how to implement them in the correct way.
I have found how to create custom Tag Helpers but not HTML Helpers. How do I create my own custom HTML Helpers?
For me I thought my HTML helpers weren't working until I spotted that the extension method is now on IHtmlHelper
not HtmlHelper
.
So for .net core:
public static IHtmlContent CheckboxListFor<TModel>(this IHtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
Instead of for .net:
public static HtmlString CheckboxListFor<TModel>(this HtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
EDIT: I've also updated the return type for .net core to be IHtmlContent as using something like HtmlContentBuilder
is a nicer way to compose HTML content and returning that returns IHtmlContent
HTML Helpers look to be supported in ASP.NET Core and are awaiting documentation:
https://docs.microsoft.com/en-au/aspnet/core/mvc/views/html-helpers
[Edit:] Since answering, the above page no longer exists. I'd say HTML Helpers, while they work, are no longer "supported" in ASP.NET Core.
Looking at the ASP.NET Core source they work fairly similarly to older versions of ASP.NET MVC:
https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/Mvc.ViewFeatures/src/Rendering/HtmlHelperDisplayExtensions.cs
Example
MyHTMLHelpers.cs:
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
namespace MyApp.Helpers
{
public static class MyHTMLHelpers
{
public static IHtmlContent HelloWorldHTMLString(this IHtmlHelper htmlHelper)
=> new HtmlString("<strong>Hello World</strong>");
public static String HelloWorldString(this IHtmlHelper htmlHelper)
=> "<strong>Hello World</strong>";
}
}
_ViewImports.cshtml (second line is the important change):
@using MyApp
@using MyApp.Helpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
MyView.cshtml:
<div>@Html.HelloWorldHTMLString()</div>
<div>@Html.HelloWorldString()</div>
Outputs:
Hello World
<strong>Hello World</strong>
Here is an example for .Net Core 2 using TagBuilders
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.IO;
public static IHtmlContent HelloWorld(this IHtmlHelper html, string name)
{
var span = new TagBuilder("span");
span.InnerHtml.Append("Hello, " + name + "!");
var br = new TagBuilder("br") {TagRenderMode = TagRenderMode.SelfClosing};
string result;
using (var writer = new StringWriter())
{
span.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
br.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
result = writer.ToString();
}
return new HtmlString(result);
}
I was never able to get HtmlHelper extension methods to work, I always recieved:
'IHtmlHelper' does not contain a definition for 'MethodName' and no extension method 'MethodName' accepting a first argument of type 'IHtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Even though I had the proper namespace in my _ViewImports.cshtml file. So I decided to use the ability of Razor pages to now support injecting services that have been registered for dependency injection. As an example I have the need to inject some values from my configuration file into my _Layout.cshtml file. So I did the following:
1) Defined a IConfigurationHelperService interface:
public interface IConfigurationHelperService
{
string GetApiUrl();
}
2) Defined an implementation of that interface in a ConfigurationHelperSerivce class (which itself is using dependency injection to get the regular configuration class):
public class ConfigurationHelperService : IConfigurationHelperService
{
public ConfigurationHelperService(IConfiguration configuration)
{
Configuration = configuration;
}
private IConfiguration Configuration { get; }
public string GetApiUrl()
{
return GetConfigurationValue(ApiUrl);
}
private string GetConfigurationValue(string key)
{
var value = Configuration[key];
if (value.IsNullOrEmpty()) throw new KeyNotFoundException($"Configruation does not contain an instance of {key}");
return value;
}
}
3) Registered the service for injection via ConfigureServices in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationHelperService, ConfigurationHelperService>();
services.AddMvc();
}
4) Added the proper namespace as a using statement into my _ViewImports.cshtml file.
5) Used the @inject keyword to define it for use in the _Layout.cshtml file.
@inject IConfigurationHelperService ConfigHelper
<!DOCTYPE html>
<html>
...
@ConfigHelper.GetApiUrl()
...
</html>
It worked great for me, and I can see a lot more uses for this on simpler pages where defining models would be too much work.