{"id":695,"date":"2016-11-03T09:49:00","date_gmt":"2016-11-03T09:49:00","guid":{"rendered":"https:\/\/staging.infragistics.com\/blogs\/?p=695"},"modified":"2025-02-25T15:30:14","modified_gmt":"2025-02-25T15:30:14","slug":"creating-custom-tag-helpers","status":"publish","type":"post","link":"https:\/\/www.infragistics.com\/blogs\/creating-custom-tag-helpers","title":{"rendered":"ASP. NET Core \u2013 Creating Custom Tag Helpers"},"content":{"rendered":"\n<p>ASP.NET Core provides a way to extend the Razor syntax with custom tags using TagHelpers. Using TagHelpers makes the Razor view much easier to understand from a non C# person due to the html look. Instead of using HTML helpers for creating a label now we can use a TagHelper.<\/p>\n\n\n\n<p>For example, if we want to create a label for our username input, we should use the following syntax.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Html.Label(\"Username\", \"Username:\", new { @class = \"control-label\" })<\/pre>\n\n\n\n<p>In order to set attributes to the label we should provide an anonymous object as third parameter. For a front end developer or non C# person this syntax may be unfamiliar and changing the class or adding additional class, or attribute requires a better knowledge of C#.<\/p>\n\n\n\n<p>The same can be achieved by using the LabelTagHelper:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;ig-loader css-path=\"http:\/\/localhost\/css\"\n           javascript-path=\"http:\/\/localhost\/css\"\n           resources=\"combo\"\/><\/pre>\n\n\n\n<p>The line above is much easier to understand and fits better in html, since it\u2019s a markup declaration rather than C# code.<\/p>\n\n\n\n<p>Because we are using TagHelpers we can take advantage of the Visual Studio IntelliSence support.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.infragistics.com\/community\/cfs-filesystemfile\/__key\/CommunityServer.Blogs.Components.WeblogFiles\/igniteui_5F00_team.November_5F00_2016\/6825.asp_5F00_net_5F00_2_5F00_pic1.png\" alt=\" Because we are using TagHelpers we can take advantage of the Visual Studio IntelliSence support\" title=\"Because we are using TagHelpers we can take advantage of the Visual Studio IntelliSence support\"\/><\/figure>\n\n\n\n<p>This looks very awesome but what about creating a custom TagHelpers, is it possible at all?<\/p>\n\n\n\n<p>The answer is yes, this is possible and you can create complex controls such as grids and dropdowns, etc.<\/p>\n\n\n\n<p>In order to create a custom TagHelper all we need to do is inherit the TagHelper class(Insert hyperlink here).<\/p>\n\n\n\n<p>Let\u2019s create TagHelper called Loader, which will load resources such as JavaScript and CSS. The Loader is an Ignite UI AMD component.<\/p>\n\n\n\n<p>We can use the template from Visual Studio from the Add new item menu.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.infragistics.com\/community\/cfs-filesystemfile\/__key\/CommunityServer.Blogs.Components.WeblogFiles\/igniteui_5F00_team.November_5F00_2016\/8625.asp_5F00_net_5F00_2_5F00_pic2.png\" alt=\" use the template from Visual Studio from the Add new item menu\" title=\"use the template from Visual Studio from the Add new item menu\"\/><\/figure>\n\n\n\n<p>And that will create the following class:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Razor.TagHelpers;\nnamespace WebApplication1.TagHelpers {\n    \/\/ You may need to install the Microsoft.AspNetCore.Razor.Runtime package into your project\n    [HtmlTargetElement(\"tag-name\")]\n    public class LoaderTagHelper: TagHelper {\n        public override void Process(TagHelperContext context, TagHelperOutput output) {}\n    }\n}<\/pre>\n\n\n\n<p>Our LoaderTagHelper will have the following structure<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;ig-loader css-path=\"http:\/\/localhost\/css\"\n           javascript-path=\"http:\/\/localhost\/css\"\n           resources=\"combo\" \/><\/pre>\n\n\n\n<p>The following will be rendered as a script tag containing html.<\/p>\n\n\n\n<p>The TagHelper should be mapped to ig-loader by changing the value of the HtmlTargetElement to \u201cig-loader\u201d. TagHelper Attributes should be mapped to C# properties, and in order to achieve this we will use the HtmlAttributeName, where we provide the name of tag helper attribute as a value.<\/p>\n\n\n\n<p>Our class should now look like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Razor.TagHelpers;\nnamespace WebApplication1.TagHelpers {\n    [HtmlTargetElement(\"ig-loader\")]\n    public class LoaderTagHelper: TagHelper {\n        [HtmlAttributeName(\"css-path\")]\n        public string CssPath {\n            get;\n            set;\n        }\n        [HtmlAttributeName(\"javascript-path\")]\n        public string JavaScriptPath {\n            get;\n            set;\n        }\n        [HtmlAttributeName(\"resources\")]\n        public string Resources {\n            get;\n            set;\n        }\n        public override void Process(TagHelperContext context, TagHelperOutput output) {}\n    }\n}<\/pre>\n\n\n\n<p>To render our TagHelper to output html we will use the Process method. We are going to set output.&nbsp;TagName to empty string and append the html to the output content.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public override void Process(TagHelperContext context, TagHelperOutput output)\n{\n    output.TagName = string.Empty;\n    output.Content.AppendHtml($\" \");\n}<\/pre>\n\n\n\n<p>This is great, but how to create more complex TagHelpers with relation between them?<\/p>\n\n\n\n<p>We can use the&nbsp;TagHelperContext.Items dictionary&nbsp;to communicate with its child TagHelper in order to create relation.<\/p>\n\n\n\n<p>Let\u2019s create a ComboTagHelper which will have a multiselection functionality.<\/p>\n\n\n\n<p>We are giving it the following structure:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;ig-combo ig-data-source=\"@Model\" ig-key=\"Id\" ig-value=\"Name\">\n    &lt;ig-combo-multi-selection ig-is-enabled=\"true\" ig-enable-checkboxes=\"true\" \/>\n&lt;\/ig-combo><\/pre>\n\n\n\n<p>We will have the following C# classes relatively:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Razor.TagHelpers;\nnamespace WebApplication1.TagHelpers {\n    [HtmlTargetElement(\"ig-combo\")]\n    public class ComboTagHelper: TagHelper {\n        [HtmlAttributeName(\"ig-key\")]\n        public string Key {\n            get;\n            set;\n        }\n        [HtmlAttributeName(\"ig-value\")]\n        public string Value {\n            get;\n            set;\n        }\n        [HtmlAttributeName(\"ig-data-source\")]\n        public object DataSource {\n            get;\n            set;\n        }\n        public MultiSelectionTagHelper MultiSelection {\n            get;\n            set;\n        }\n        public override void Process(TagHelperContext context, TagHelperOutput output) {}\n    }\n}<\/pre>\n\n\n\n<p>and<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Razor.TagHelpers;\nnamespace WebApplication1.TagHelpers {\n    [HtmlTargetElement(\"ig-combo-multi-selection\")]\n    public class MultiSelectionTagHelper: TagHelper {\n        [HtmlAttributeName(\"ig-enable-checkboxes\")]\n        public bool EnableCheckBoxes {\n            get;\n            set;\n        }\n        [HtmlAttributeName(\"ig-is-enabled\")]\n        public bool Enabled {\n            get;\n            set;\n        }\n        public override void Process(TagHelperContext context, TagHelperOutput output) {}\n    }\n}<\/pre>\n\n\n\n<p>In order to create a relation between the TagHelpers and initialize MultiSelection property in the parent with correct context we will pass the instance of the parent in the items collection of its context.<\/p>\n\n\n\n<p>In our parent process method, we are going to pass this to key ComboTagHelper and get child context so its attributes can be parsed.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public async override void Process(TagHelperContext context, TagHelperOutput output)\n{\n    context.Items.Add(typeof(ComboTagHelper), this);\n    await output.GetChildContentAsync();\n    \/\/ Set the output of the TagHelper\n}<\/pre>\n\n\n\n<p>In the child process we will set a reference to MultiSelection property of the parent and suppress the output.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public override void Process(TagHelperContext context, TagHelperOutput output)\n{\n    ((ComboTagHelper)context.Items[typeof(ComboTagHelper)]).MultiSelectionSettings = this;\n    output.SuppressOutput();\n}<\/pre>\n\n\n\n<p>Now we have our child TagHelper processed as property of the parent and we can use it to render the output.<\/p>\n\n\n\n<p>The TagHelpers are available in Volume release 16.2 of&nbsp;<a href=\"http:\/\/www.igniteui.com\/\" rel=\"noreferrer noopener\" target=\"_blank\">Ignite UI<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Asp.net Core provides a way to extend the Razor syntax with custom tags using TagHelpers.<\/p>\n","protected":false},"author":99,"featured_media":1219,"comment_status":"publish","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17],"tags":[],"class_list":["post-695","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to"],"_links":{"self":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/users\/99"}],"replies":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/comments?post=695"}],"version-history":[{"count":4,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/695\/revisions"}],"predecessor-version":[{"id":2125,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/695\/revisions\/2125"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/media\/1219"}],"wp:attachment":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/media?parent=695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/categories?post=695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/tags?post=695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}