Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

TagHelpers in MVC v(less than)Next

0.00/5 (No votes)
7 Mar 2015 2  
This article describes a generic tag helpers library Xania.AspNet.TagHelpers (v1.0.11-beta at the time of writing) that support custom tags which works with all ASP.NET MVC versions.

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-betaonly 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

  1. To use the code first add a reference to nuget package Xania.AspNet.TagHelpers
  2. Implement an custom tag helper.
    C#
    public class HelloTagHelper : TagHelperBase
    {
        public override void RenderBeforeContent(TextWriter writer)
        {
           writer.Write("<div>Hello");
        }
    
        public override void RenderAfterContent(TextWriter writer)
        {
           writer.Write("!</div>");
        }
    }
  3. 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) }
            });
        }
    }
  4. Insert a <hello> tag into you html
    <hello>World</hello> 
  5. 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.

C#
    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. 

C#
[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.

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here