DotNetCurry Logo

Creating Tag Helpers in ASP.NET MVC 6 / ASP.NET Core 1.0

Posted by: Mahesh Sabnis , on 3/24/2016, in Category ASP.NET MVC
Views: 22318
Abstract: Tag Helpers in ASP.NET Core 1.0/MVC 6 helps you to write server-side logic to generate Html contents and use it on Razor Views. This makes the View cleaner and easier to maintain.

ASP.NET MVC 6 comes with an inbuilt support for dependency injection, defining models decoupled from controllers, and many other features. Please visit following links to read more about MVC 6.

In this article, we will explore another new feature of MVC 6 called as the Tag Helper.

 

Section 1: Tag Helper

What is Tag Helper?

Tag Helper is a new feature in ASP.NET MVC 6 that enables the server-side code to create and render HTML elements, in MVC Razor View files. These are the objects which can be bound to the Models and based on these properties, HTML elements can be dynamically rendered. We have built-in tag helper objects like:

  • asp-action - for action methods
  • asp-route-id - for route expression
  • asp-validation-summary - for validations
  • asp-for - for model binding
  • and many more

The advantage of these tag helpers makes the MVC view cleaner.

Tag Helper Advantages

· Since Tag helpers provides HTML-friendly development experience and look like standard HTML, the front-end designers who are working on JavaScript libraries/frameworks and CSS, etc. can easily change/update View without knowing the C# programming language.

· Visual Studio 2015 provides intellisense for Tag helpers while using them in a View.

· Tag helper provide more reliable and reusable code that can be created and used across different views, as and when it is needed.

How are Tag helpers different from Html Helper Methods in earlier releases of MVC?

In the earlier versions of MVC (version 5 and below), we have Html helper extension methods. These are invoked as executable methods on Razor View, based on the parameters passed to it. We use @ symbol on the Razor view to invoke Html helper methods. This symbol represents the start of the method invocation on the view.

Consider the following syntax:

@Html.LabelFor(model => model.Salary, htmlAttributes: new { @class = "control-label col-md-2" })

In the above code, we are using LabelFor html helper method. This is bound to the Salary property of the model using lambda expression. The second parameter passed to it is an anonymous object which contains @class property. We have to define it in this way because class is the standard keyword in C# language. This increases the coding complexity. In this case, if the front-end developer who is not having C# knowledge and wants to modify the view containing html helper, may get confused and mess up.

Tag Helpers, on the other hand, are integrated in the HTML element and provide intellisense. The most important feature of the Tag Helper is that they can be independent from the C# syntax.

Consider the following line

In the above case, the asp-for is a tag helper, which is bound to the CategoryId property of the Model passed to the view. This is independent from the C# programming language.

How the Tag Helpers are managed in MVC 6

In a MVC 6 project, we have to use the Microsoft.AspNet.Mvc.TagHelpers assembly. This is used to manage all inbuilt tag helpers in the application. In the MVC application, in Views folder, add an MVC View Imports Page, (_ViewImports.cshtml). In this page, using @addTagHelper we can add the Microsoft.AspNet.Mvc.TagHelpers assembly. In the project, if we create custom tag helpers, then we need to add the project assembly in the _ViewImports.cshtml file.

Section 2: Creating Custom Tag Helper

We will implement our application using the free Visual Studio 2015 Community edition.

Step 1: Open Visual Studio 2015 and create a new ASP.NET Web Application as shown in the following image

new-web-project

Name this project as MVC6_TagHelpers. Click on Ok, this will show the following window. Select Empty template and check the MVC checkbox.

empty-mvc

Step 2: The project contains project.json. We have to add the necessary dependencies in this project to create MVC application as shown in the following code (highlighted)

"dependencies": {
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "Microsoft.AspNet.Razor": "4.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.Razor": "6.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
    "Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
    "Microsoft.Extensions.CodeGenerators.Mvc":"1.0.0-rc1-final",   
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final"
  },

Here we are using Microsoft.AspNet.Mvc and Microsoft.AspNet.Mvc.TagHelpers.

Step 3: We also have a Startup.cs class file with Startup class. This class is an entry point to the MVC application. We will add the following code in ConfigureServices() and Configure() methods to use MVC Service and Route. (highlighted)

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
    app.UseIISPlatformHandler();

    app.UseMvc(routes =>
    {
        routes.MapRoute(name:"default",template:"{controller=Home}/{action=Index}/{id?}");
    });
}

Step 4: In the project, add Models, Views and Controllers folders. Add the following Model classes in the Models folder by adding a class file in it.

using System.Collections.Generic;

namespace MVC_TagHelper.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }

        public decimal Price { get; set; }
    }

    public class ProductStore : List
    {
        public ProductStore()
        {
            Add(new Product() { ProductId = 1, ProductName = "P1",Price=3000 });
            Add(new Product() { ProductId = 2, ProductName = "P2", Price = 3100 });
            Add(new Product() { ProductId = 3, ProductName = "P3", Price = 3400 });
            Add(new Product() { ProductId = 4, ProductName = "P4", Price = 3700 });
            Add(new Product() { ProductId = 5, ProductName = "P5", Price = 4000 });
            Add(new Product() { ProductId = 6, ProductName = "P6", Price = 3200 });
        }
    }
}

The above code shows a Product Entity class and the ProductStore class.

Step 5: In the Controllers folder, add a new MVC Controller Class by Right-Click > Add-New Item > MVC Controller Class. We will keep the name of it as HomeController.cs. Add the following code in this class.

using Microsoft.AspNet.Mvc;
using MVC_TagHelper.Models;

// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860

namespace MVC_TagHelper.Controllers
{
    public class HomeController : Controller
    {
        // GET: //
        public IActionResult Index()
        {
            var products = new ProductStore(); 
            return View(products);
        }
    }
}

The index action method returns Product List to the View.

 

Step 6: In the project, create a new folder of name CustomTagHelper. In this folder, add a new class file of name TableTagHelper.cs. Add the following code in this class file:

using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.TagHelpers;
using System.Collections;
using System.Text;
using System.Threading.Tasks;

using System.Reflection;

namespace MVC_TagHelper.CustomTagHelper
{
    //1.
    [HtmlTargetElement("table",Attributes ="generate-rows, source-model")]
    public class TableTagHelper : TagHelper
    {
        //2.
        [HtmlAttributeName("generate-rows")]
        public int RepeatCount { get; set; }
        //3.
        [HtmlAttributeName("source-model")]
        public ModelExpression DataModel { get; set; }
        //4.
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            //4a
            IEnumerable model = DataModel.Model as IEnumerable;
            //4b
            if (model == null)
            {
                return;
            }
            else
            {
             //4c
                StringBuilder sb = new StringBuilder();
                //4d
                foreach (var m in model)
                {
                    PropertyInfo[] properties = m.GetType().GetProperties();
 
                    
                    //4e
                    string html = "";
                    for (int i = 0; i < properties.Length; i++)
                    {
                        html += "" + m.GetType().GetProperty(properties[i].Name).GetValue(m, null) + "";
                    }
                    html += "";

                    sb.Append(html);
                } 

                //4f
                output.Content.SetHtmlContent(sb.ToString());
            }
        }
    }
}

The above class has the following specifications (Note following line numbers matches with comments applied in the code)

1. The class TableTagHelper is derived from the TagHelper class. The TagHelper class is used to apply HTML attribute on the matching HTML element. In our case, we have applied the HtmlTergetElement attribute on the TableTagHelper class. This attribute class accepts the first parameter as the name of Html element to target, in current example we are targeting the html table element. The second parameter is the attribute parameter which must be set on the target element to execute. In the current example, we have used generate-rows and source-model. These attributes will be executed on the html table element.

2. Defines RepeatCount property which will be used to generate rows in the table.

3. The DataModel property will be used to accept the LINQ expression passed to it using ModelExpression class. In our case, it will be the Model object passed to the Razor View.

4. The ProcessAsync() method of the TagHelper base class is overridden to contain server-side logic to generate Html. This accepts two parameters. The first is TagHelperContext object which is used to define execution of the TagHelper in the current context. The TagHelperOutput class is used to define Html output when the TagHelper is executed.

Please check the code comments 4a to 4f.

a. This line reads the Model passed to the TagHelper when applied to the target element on Razor view.

b. This line checks if the Model passed to View is null, if yes the methods returns.

c. Defines an instance of the StringBuilder class to contain html elements generated dynamically after execution.

d. Iterates through the Model data to perform server-side operations.

e. Generates html table rows and cells based on the properties and values of those properties from the Model.

f. The generated Html string is passed to the TagHelperOutput class as Html content.

Step 7: To use the TagHelper, add a new _ViewImports.cshtml file in the Views folder and add the following code in it:

@using MVC_TagHelper;
@using MVC_TagHelper.Models;

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "*, MVC_TagHelper"

In the above code, we are adding the built-in tag helpers using Microsoft.AspNet.Mvc.TagHelpers assembly and to execute the custom Tag helper, we are adding application assembly i.e. MVC_TagHelper.

Step 8: In the View Folder, add a new folder of name Home, in this folder add a new MVC View Page of name Index.html (right-click > Add > New Item > MVC View Page). In this view add the

Tag. When we try to add the tag helpers for the
, here is where we can experience the advantage of the TagHelper as shown in the following image

intellisense-generate-row

Complete the view markup code as shown in the following code

@model IEnumerable



 

Run the application, the generate-rows attribute will generate table rows based on the records in the Model property and the source-model will be used to display data in the table as shown in the following image

tag-helper-result

Conclusion: The Tag Helpers in ASP.NET Core 1.0 and MVC 6 provides facility for developers to write server-side logic to generate Html contents and use it on Razor Views. This makes the View cleaner and easier to maintain.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!