Background
MVC 6
With the arrival of MVC 6 we are finally getting back the custom tags aka TagHelpers (article) like we used to have with WebForms Custom Controls, but without it's disadvantages (article). The syntax of the new html tag helpers could be as simple and clean the following example.
<panel>
content
</panel>
This is the syntax I was waiting for for so long, and to be honest I don't understand why it took Microsoft Team more than 7 years (since first CTP release DEC 2007) to add support for custom tag helpers, and even worse it's not supported in MVC 5 (not to mention MVC 1-4).
MVC < 6
There is nothing like TagHelpers for older versions of MVC. Currenty there are a lot of Web Projects that use MVC 3, 4 or 5 and investing heavily in those projects so I don't expect (large) companies to move any soon to ASP.NET MVC 6. Which would mean that we have to live with Html Extensions for yet another 7 years (ok maybe i am exaggerating, but you get the idea).
Generic tag helpers library
Since a couple of weeks I started a new generic tag helpers project on github named <a href="https://github.com/ibrahimbensalah/Xania.AspNet">Xania.AspNet.TagHelpers</a>
. And by generic I mean not bound to any version of MVC.
Core featues:
- The custom tags is first class, composable tag element that you can put anywhere in your view (razor, aspx or simply plain html) and the inner content might contain anything, e.g. ustom tags can be combined Html Extension methods.
- The tag helper class (Code Behind if you would like) is being build using your favorite IoC framework, thus you can inject dependencies into the custom tag helper.
- The tag helper class could also inject action related object, e.g.
RequestContext
- The custom tag attributes are mapped to properties of the tag helpers class. In current version v1.0.11-beta, only
string
properties are supported, but the plan is to use Asp.Net's ModelBinding API to bind to complex types.
Using the Code
Quick start
- To use the code first add a reference to nuget package Xania.AspNet.TagHelpers.
- Implement an custom tag helper.
public class HelloTagHelper : TagHelperBase
{
public override void RenderBeforeContent(TextWriter writer)
{
writer.Write("<div>Hello");
}
public override void RenderAfterContent(TextWriter writer)
{
writer.Write("!</div>");
}
}
- Register
TagHelperFilterAtttribute
to the global filters.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new TagHelperFilterAttribute
{
TagHelpers = new []{ typeof(HelloTagHelper) }
});
}
}
- Insert a
<hello>
tag into you html
<hello>World</hello>
- That's it, running the application will now generate
<div>Hello World!</div>
Mvc Integration
To hook up the tag helpers in an AspNet Web Application we use the TagHelperFilterAttribute
which implement the ActionFilterAttribute.
By default, the DependencyResolver.Current
instance is used as dependency resolver for tag helper's constructor parameters. You can also provide a dependency resolver explicitly:
filters.Add(new TagHelperFilterAttribute(new MyDependencyResolver())
{
...
});
The list of custom tag helpers TagHelperFilterAttribute.TagHelpers
might be retrieved by scanning all types in your library project and selecting only the types which implement the ITagHelper interface.
filters.Add(new TagHelperFilterAttribute()
{
TagHelpers = GetTypes<ITagHelper>(typeof(FilterConfig).Assembly)
});
...
static IEnumerable<Type> GetTypes<T>(Assembly assembly)
{
return assembly.GetTypes().Where(type => typeof(T).IsAssignableFrom(type));
}
The custom tag helper
Every custom tag helper must implement the ITagHelper
. The tag helper could also inject dependencies using constructor parameters.
public HelloTagHelper(IHelloService helloService)
{ ... }
Yould could inject
- Any service object provided by
IDependencyResolver
(see section Mvc Integration) or - Action based objects:
ControllerContext
, RequestContext
, UrlHelper
, ViewData
and TempData
Tag helpers, when registered, are added to the filter chain and intercept all inner content of the view tag itself. By default, the name tag helper class without the postfix "TagHelper" is mapped to the name of the view tag and the attribute are mapped to class properties. You can also specify different tag name using TagNameAttribute.
To add custom transformation of the inner content then implement the RenderContent
method.
[TagName("hello", "hallo")]
public class HelloTagHelper : TagHelperBase
{
public HelloTagHelper(IHelloService helloService)
{ ... }
public override void RenderBeforeContent(TextWriter writer)
{
writer.Write("<div>Hello");
}
public override void RenderAfterContent(TextWriter writer)
{
writer.Write("!</div>");
}
public override void RenderContent(TextWriter writer, char ch)
{
base.RenderContent(writer, Char.ToUpper(ch));
}
}
Conclusion
The generic tag helpers library offers only core functionality to create custom view tags, but you can use it as an addition to the existing HtmlHelper<TModel>
based extensions.
Also, there is a lot of work to do to add out-of-the-box support for basic html elements like <a>
, <input>
and popular css framework like bootstrap<font color="#111111" face="Segoe UI, Arial, sans-serif"><span style="font-size: 14px;"> and </span></font>
kendoUI.
So I hope to receive your feedback to help improve this library.