Ask Learn
Preview
Ask Learn is an AI assistant that can answer questions, clarify concepts, and define terms using trusted Microsoft documentation.
Please sign in to use Ask Learn.
Sign inThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this post we will develop a custom Tag Helper to generate the basic HTML table based on the method name passed as a parameter. The idea is that our custom Tag Helper will take a method name as a parameter that will be executed by its code behind class and generate the HTML table based on the list returned from some source.
Tag Helpers is one of the exciting feature introduced in ASP.NET 5. Tag Helpers is an alternative to HTML Helpers introduced in previous versions of ASP.NET to produce dynamic HTML markup on the page at runtime.
Benefits of using Tag Helpers than HTML Helpers
Here is an example showing how we can bind Email property of the Model associated to the Page using HTML Helper and Tag Helper.
@Html.EditorFor (i => i.Email, new {htmlAttributes = new {@class = "form-control"}})
<input asp-for="Email" class="form-control" />
There are many out of the box Tag Helpers provided in ASP.NET 5 that can be used with asp- prefix. You can use existing or build custom tag helpers by adding a NuGet package Microsoft.AspNet.Mvc.TagHelpers. Once you add this package, you can create custom tag helpers by deriving your class from TagHelper. TagHelper is an abstract class and contains virtual methods i.e. Process and ProcessAsync to facilitate both synchronous and asynchronous code execution that can be overridden in your custom Tag Helper class.
Following is the code snippet of the TagHelper class
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
//
// Summary:
// Class used to filter matching HTML elements.
public abstract class TagHelper : ITagHelper
{
protected TagHelper();
//
// Remarks:
// Default order is 0.
public virtual int Order { get; }
//
// Summary:
// Synchronously executes the Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelper
// with the given context and output.
//
// Parameters:
// context:
// Contains information associated with the current HTML tag.
//
// output:
// A stateful HTML element used to generate an HTML tag.
public virtual void Process(TagHelperContext context, TagHelperOutput output);
//
// Summary:
// Asynchronously executes the Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelper
// with the given context and output.
//
// Parameters:
// context:
// Contains information associated with the current HTML tag.
//
// output:
// A stateful HTML element used to generate an HTML tag.
//
// Returns:
// A System.Threading.Tasks.Task that on completion updates the output.
//
// Remarks:
// By default this calls into Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelper.Process(Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelperContext,Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelperOutput).
[AsyncStateMachine(typeof(<ProcessAsync>d__4))]
public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
}
Now let’s create a custom tag helper by following the steps below
Note: We can define multiple attributes using comma. Like Attribute =”attribute-A, attribute-B, attribute-C”
[TargetElement("grid", Attributes ="grid-datasource")]
public class GridTagHelper: TagHelper
[HtmlAttributeName("grid-datasource")]
public string GridDataSource { set; get; }
public static class DataManager
{
public static List<Person> GetPersons()
{
List<Person> lst = new List<Person>();
lst.Add(new Person { Id = 1, Name = "Ovais Mehboob", Position = "Solution Architect" });
lst.Add(new Person { Id = 2, Name = "Khusro Habib", Position = "Development Manager" });
lst.Add(new Person { Id = 3, Name = "David Salcedo", Position = "Hardware Consultant" });
lst.Add(new Person { Id = 4, Name = "Janet Bauer", Position = "Sales Director" });
lst.Add(new Person { Id = 5, Name = "Asim Khan", Position = "Software Engineer" });
return lst;
}
}
public class Person
{
public int Id { set; get; }
public string Name { set; get; }
public string Position { set; get; }
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Defining table, thead and tr tags
TagBuilder controlBuilder = new TagBuilder("table");
TagBuilder thead = new TagBuilder("thead");
TagBuilder rowHeader = new TagBuilder("tr");
//Getting type of our DataManager class used to return generic lists
Type type = typeof(DataManager);
//Reading DataManager GetPersons as defined in the HTML markup
MethodInfo info=type.GetMethod(GridDataSource);
//Invoking DataManager GetPersons method
object lst = info.Invoke(null, null);
//Reading List Type returned from GetPersons
Type listType=Type.GetType(((System.Reflection.TypeInfo)(lst.GetType()).GenericTypeArguments[0]).FullName);
//Getting properties for List Type
var properties = listType.GetProperties();
//Looping through each property and setting Header Name based on property name
foreach (var property in properties)
{
TagBuilder col = new TagBuilder("td");
col.InnerHtml = property.Name.ToString();
rowHeader.InnerHtml += col.ToString();
}
thead.InnerHtml += rowHeader.ToString();
controlBuilder.InnerHtml = thead.ToString();
//Now defining rows and columns
TagBuilder tbody = new TagBuilder("tbody");
//Casting list object to IList
IList collection = (IList)lst;
//Looping through each item in the list
for(int i=0;i<collection.Count; i++)
{
//Initialized Tag Builder for Table row
TagBuilder row = new TagBuilder("tr");
var props =collection[i].GetType().GetProperties();
foreach(var property in props)
{
//Initialized Tag Builder for Table column
TagBuilder col = new TagBuilder("td");
col.InnerHtml = property.GetValue(collection[i]).ToString();
row.InnerHtml += col.ToString();
}
tbody.InnerHtml += row.ToString();
}
controlBuilder.InnerHtml += tbody.ToString();
output.Content.Append(controlBuilder.ToHtmlString(TagRenderMode.Normal).ToString());
}
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System;
using System.Collections;
using System.Reflection;
namespace TagHelperProj.TagHelpers
{
[TargetElement("grid", Attributes ="grid-datasource")]
public class GridTagHelper: TagHelper
{
[HtmlAttributeName("grid-datasource")]
public string GridDataSource { set; get; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Defining table, thead and tr tags
TagBuilder controlBuilder = new TagBuilder("table");
TagBuilder thead = new TagBuilder("thead");
TagBuilder rowHeader = new TagBuilder("tr");
//Getting type of our DataManager class used to return generic lists
Type type = typeof(DataManager);
//Reading DataManager GetPersons as defined in the HTML markup
MethodInfo info=type.GetMethod(GridDataSource);
//Invoking DataManager GetPersons method
object lst = info.Invoke(null, null);
//Reading List Type returned from GetPersons
Type listType=Type.GetType(((System.Reflection.TypeInfo)(lst.GetType()).GenericTypeArguments[0]).FullName);
//Getting properties for List Type
var properties = listType.GetProperties();
//Looping through each property and setting Header Name based on property name
foreach (var property in properties)
{
TagBuilder col = new TagBuilder("td");
col.InnerHtml = property.Name.ToString();
rowHeader.InnerHtml += col.ToString();
}
thead.InnerHtml += rowHeader.ToString();
controlBuilder.InnerHtml = thead.ToString();
//Now defining rows and columns
TagBuilder tbody = new TagBuilder("tbody");
//Casting list object to IList
IList collection = (IList)lst;
//Looping through each item in the list
for(int i=0;i<collection.Count; i++)
{
//Initialized Tag Builder for Table row
TagBuilder row = new TagBuilder("tr");
var props =collection[i].GetType().GetProperties();
foreach(var property in props)
{
//Initialized Tag Builder for Table column
TagBuilder col = new TagBuilder("td");
col.InnerHtml = property.GetValue(collection[i]).ToString();
row.InnerHtml += col.ToString();
}
tbody.InnerHtml += row.ToString();
}
controlBuilder.InnerHtml += tbody.ToString();
output.Content.Append(controlBuilder.ToHtmlString(TagRenderMode.Normal).ToString());
}
}
}
@addTagHelper "*, TagHelperProj"
<grid grid-datasource="GetPersons">
</grid>
To learn more about Tag Helpers, please visit https://docs.asp.net/projects/mvc/en/latest/views/tag-helpers/authoring.html
Hope this helps!
Anonymous
June 18, 2015
The comment has been removed
Anonymous
June 18, 2015
The comment has been removed
Anonymous
June 20, 2015
creating controls reinvented once again, cool!
Anonymous
July 16, 2015
The first comparison of EditorFor and <input> tag is incorect, as EditorFor is abstract - it can be anything. It should be TextBoxFor that generates input with text type.
This new stuff looks less hacky & more htmlish, should be good. (No more anonymous types with non-resolving css classes)
Anonymous
July 20, 2015
I don't agree with you Aurimas. Both Html.EditorFor and asp-for works as per the property type associated. So for example, if property type is string, textbox is generated otherwise in case of Boolean, checkbox is generated and etc.
Secondly, I tried to show the way how one can develop something like a generic control that can be used with any list of objects. However, it may involve complexity during development but requires one time effort. This is more helpful if you have different list of objects and you want to develop some custom grid or control to provide same behavior or functionality
Anonymous
September 16, 2015
See Authoring Tag Helpers by Rick Anderson for an updated tutorial. docs.asp.net/.../authoring.html
Ask Learn is an AI assistant that can answer questions, clarify concepts, and define terms using trusted Microsoft documentation.
Please sign in to use Ask Learn.
Sign in