TAG Helpers
- What are tag helpers? Advantages
- Tag helper vs HTML helper.
- Why use tag helpers?
- Tag helper example.
- Tag helper steps.
- Tag helper class.
- Html tag helpers.
- Complex tag helper.
What are Tag Helpers? Advantages of HTML Helper
In our view we need some controls are HTML elements like form controls are heading elements that help us to build View. Tag helpers help us to create HTML elements, we can extend existing HTML elements or we can have our own custom tags, Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.
Examples of tag helpers
<a asp-action="ActionName" asp-controller="ControllerName" class="customCssClass">
</a>
We already have some built-in tag helpers which help us to have eat and clean Views. Like Label, Anchor, and Form Controls like input type, select, etc.
The advantage of using tag helper over HTML Helper is that it makes things really neat in the above example we use a simple html tag attribute class=”customCssClass” but in the case of HtmlHelper we need to use special characters like
new {@class="caption"}
Some advantages of using Tag Helpers
- An HTML-friendly development experience.
- A rich IntelliSense environment for creating HTML and Razor markup.
- A way to make you more productive and able to produce more robust, reliable, and maintainable code using information only available on the server.
Scope of Tag Helpers, How can we use them in our project?
You should have Microsoft.AspNetCore.Mvc.TagHelpers to use tag helpers in your project, whenever you create a project from a template Razor tools are installed so you don’t need to worry about it.
To use tag helpers in your view you need to have a namespace in your views, It is good to import the namespace in the _ViewImports.cshtml file.
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Now you have intelligent support of tag helper in your views.
If you have your custom tag defined in the folder “TagHelpers” in your project then you can import the tag helpers folder like.
@addTagHelper *, Tag_Helpers
This “Tag_Helpers” is the main namespace. Now it imports folders in views so you can declare your own custom tag helpers in view without worrying about including reference on each view.
@addTagHelper "*, projectname"
@removeTagHelper can also used to remove specific tag helpers of a namespace.
We can say that there are different types of Tag Helper
- Tag helper that extends existing HTML tag.
- We have already used an anchor tag that takes asp-action and asp-controller as a tribute and we render links according to the provided attributes.
- Custom tag helper.
- Later in this, we will define a custom tag helper that renders a list of objects. We will use a custom tag helper for that, which takes a list of strings as the parameter.
- Tag helper that helps to do some operations seamlessly and update some existing tags or their attributes.
- These tag helpers are link environments, in the environment tag we can have a group of files that need to be included on the basis of the environment if the environment is developed get a local javascript file but in the case of production get files from CDN.
Custom Tag Helper
Custom tag helper can help you to have a tag helper that can perform some operations as if you want to have a header tag just for example.
Step 1. Decide what Is your target tag and then create a class that should inherit from TagHelper. Please note that naming convention is important, if you have multiple words in a name like mygrid then it can be used as my-grid in view (Kebab-Style). MyGridTagHelper class makes the runtime target the tag like my-grid. If you have a simple class name like AlertTagHelper then you can simply use alert as the tag name.
Step 2. Override the Process or ProcessAsync method that gives you output context to perform the operation.
- TagHelperContext This parameter contains information about the tag addressed by the tag helper including all its attributes and children elements.
- TagHelperOutput is Used to generate the tag output.
Step 3. Use the tag in your view and pass the required attribute.
Custom tag helper example
- Target the div element which has an info-message attribute.
- Check the properties and render the bootstrap info message.
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Tag_Helpers.TagHelpers
{
[HtmlTargetElement("div", Attributes = InfoMessageAttributeName)]
public class AlertMessageTagHelper : TagHelper
{
private const string InfoMessageAttributeName = "info-message";
private const string MessageAttributeName = "message";
private const string HideAttributeName = "visible";
private const string AlertTypeAttributeName = "alert-type";
[HtmlAttributeName(MessageAttributeName)]
public string Message { get; set; }
[HtmlAttributeName(HideAttributeName)]
public bool Visible { get; set; }
[HtmlAttributeName(AlertTypeAttributeName)]
public string AlertType { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
string className = string.Empty;
if (Visible)
{
className = "show";
}
else
{
className = "hidden";
}
Debug.WriteLine("========================>" + context.UniqueId);
string InfoMessageContent = $@"
<div id='{context.UniqueId}' onclick='hideContent(this)' class='infoMessage alert alert-dismissable {AlertType} {className}'>
<a href='#' class='close' data-dismiss='alert' aria-label='close'>×</a>
<strong>Information:</strong><span id='statusMessage'>{Message}</span>
</div>";
string js = "<script type='text/javascript'>function hideContent(e) { $(e).removeClass('show').addClass('hidden'); } function showMessage(message) { var control = $('[info-message]'); $(control).find('span').text(message); }</script>";
output.Content.AppendHtml(js + InfoMessageContent);
base.Process(context, output);
}
}
}
Let me share how it can be used in View,
@using Tag_Helpers.Controllers;
@model IEnumerable<Tag_Helpers.Controllers.Data>
{
ViewData["Title"] = "Home Page";
}
<script type="text/javascript">
function UpdateMessageContent() {
var message = $("#txt1").val();
showMessage(message);
}
</script>
<div info-message message="A simple message" visible="true" alert-type="alert-info"></div>
<input type="text" id="txt1" />
<input type="button" onclick="UpdateMessageContent()" value="Update Message Content" />
Output
When I add some text in the textbox and click on the Update Message Button then it updates the message content.
Explanation
We declared a class AlertMessageTagHelper, this class targets HtmlElement div so we have declared attributes of class like.
[HtmlTargetElement("div", Attributes = InfoMessageAttributeName)]
// And
private const string InfoMessageAttributeName = "info-message";
We have defined class property along with HtmlAttribute that is required to target,
private const string MessageAttributeName = "message";
[HtmlAttributeName(MessageAttributeName)]
public string Message
{
get;
set;
}
So we have other properties like that and we follow the same procedure.
When we have any div html element with the info-message attribute then it targets this tag helper.
We pass the message, visible, and alert type attributes, alert type attributes can be alert-danger, alert-info, etc.
<div info-message message="A simple message" visible="true" alert-type="alert-info"></div>
Example of a custom Tag Helper
The objective of this tag is to render html table which is passed from view by using grid tag helper.
This is a custom tag helper example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Reflection;
namespace Tag_Helpers.TagHelpers
{
[HtmlTargetElement("grid")]
public class GridTagHelper : TagHelper
{
private const string ItemsAttributeName = "items";
[HtmlAttributeName(ItemsAttributeName)]
public IEnumerable<object> Items { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
TagBuilder table = new TagBuilder("table");
table.GenerateId("id", context.UniqueId);
var attributes = context.AllAttributes.Where(atr => atr.Name != ItemsAttributeName).ToDictionary(atr => atr.Name, atr => atr.Value);
table.MergeAttributes<string, object>(attributes);
var tr = new TagBuilder("tr");
var heading = Items.First();
PropertyInfo[] properties = heading.GetType().GetProperties();
foreach (var prop in properties)
{
TagBuilder headerCol = new TagBuilder("th");
headerCol.InnerHtml.Append(prop.Name.ToString());
tr.InnerHtml.AppendHtml(headerCol);
}
table.InnerHtml.AppendHtml(tr);
foreach (var item in Items)
{
tr = new TagBuilder("tr");
foreach (var prop in properties)
{
TagBuilder col = new TagBuilder("td");
col.InnerHtml.Append(prop.GetValue(item).ToString());
tr.InnerHtml.AppendHtml(col);
}
table.InnerHtml.AppendHtml(tr);
}
output.Content.SetHtmlContent(table);
}
}
}
We can define a class in a project.
public class Test
{
public string Name {
get;
set;
}
public string Email {
get;
set;
}
}
We can use that class in View like, don’t forget to import the class namespace in View if it is not accessible in View.
@{
List<Test> tests = new List<Test>();
Test t1 = new Test();
t1.Name = "Khuram";
t1.Email = "[email protected]";
Test t2 = new Test();
t2.Name = "Khuram";
t2.Email = "[email protected]";
tests.Add(t1);
tests.Add(t2);
}
<grid items="@tests" border="1"></grid>
Output in the view is like this
Explanation
We have declared a class and use.
[HtmlTargetElement("grid")]
public class GridTagHelper : TagHelper
Now this class targets a custom tag which is <grid> this grid tag expects some attributes.
private const string ItemsAttributeName = "items";
[HtmlAttributeName(ItemsAttributeName)]
public IEnumerable<object> Items
{
get;
set;
}
Items is the attribute name that should be passed from view, it is a collection of objects and that object can be a class like we pass object collection of class Test.
TagBuilder table = new TagBuilder("table");
table.GenerateId("id", context.UniqueId);
We build a tag using the TagBuilder class and assign the unique ID to the table.
var attributes = context.AllAttributes
.Where(atr => atr.Name != ItemsAttributeName)
.ToDictionary(atr => atr.Name, atr => atr.Value);
table.MergeAttributes<string, object>(attributes);
If there is any attribute defined on the grid tag then merge it.
var tr = new TagBuilder("tr");
var heading = Items.First();
PropertyInfo[] properties = heading.GetType().GetProperties();
foreach (var prop in properties)
{
TagBuilder headerCol = new TagBuilder("th");
headerCol.InnerHtml.Append(prop.Name.ToString());
tr.InnerHtml.AppendHtml(headerCol);
}
table.InnerHtml.AppendHtml(tr);
Create a row and for each property of a class, create a header cell of a table.
foreach (var item in Items)
{
tr = new TagBuilder("tr");
foreach (var prop in properties)
{
TagBuilder col = new TagBuilder("td");
col.InnerHtml.Append(prop.GetValue(item).ToString());
tr.InnerHtml.AppendHtml(col);
}
table.InnerHtml.AppendHtml(tr);
}
Create a row for an object and populate data.
output.Content.SetHtmlContent(table);
Render the table.
Complex Tag Helper
Sometimes things are not simple so we need to have a complex tag helper, where we can have a tag inside the tag or a parent tag can have child tags.
Example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Tag_Helpers.TagHelpers
{
[HtmlTargetElement("combo")]
public class ComboTagHelper : TagHelper
{
[HtmlAttributeName("key")]
public string Key { get; set; }
[HtmlAttributeName("value")]
public string Value { get; set; }
[HtmlAttributeName("dsource")]
public object DataSource { get; set; }
[HtmlAttributeName("dclass")]
public string DataClass { get; set; }
public MultiSelectionTagHelper MultiSelection { get; set; }
public override async void Process(TagHelperContext context, TagHelperOutput output)
{
context.Items.Add(typeof(ComboTagHelper), this);
var childContent = await output.GetChildContentAsync();
//Render items here
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Tag_Helpers.TagHelpers
{
[HtmlTargetElement("combo-multi-selection")]
public class MultiSelectionTagHelper : TagHelper
{
[HtmlAttributeName("enable-checkboxes")]
public bool EnableCheckBoxes { get; set; }
[HtmlAttributeName("is-enabled")]
public bool Enabled { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
((ComboTagHelper)context.Items[typeof(ComboTagHelper)]).MultiSelection = this;
output.SuppressOutput();
}
}
}
In the view we have.
<combo dsource="@Model" key="Id" value="Name">
<combo-multi-selection is-enabled="true" enable-checkboxes="true" dclass="Data" />
</combo>
In the parent <combo> tag we have another tag that specifies the multi-selection and data class by class attribute.
We can get the child tag properties by.
Defining child tag helper class object in the parent class.
Add the parent class in the current context.
Get the class object from the context in the child tag helper class.
Now you can assign the child tag helper object to the parent.
Reference