كيفية عمل Tag Helpers Custom حسب الاحتياج

-






قد تحتاج في بعض الأحيان الى انشاء أدوات مخصصه في التطبيق، بحيث يتم تطويرها وانشاءها حسب الحاجة، وغير مرتبطة بالأدوات المبنية داخل ASP.NET Core. لذلك سنتعلم في هذا الدرس كيفية انشاء مثل هذه الأدوات وكيفية استخدامها.

افتح المشروع الخاص بنا StudentsAcademy ثم قم أولاً بإنشاء مجلد TagHelpers داخل التطبيق كما في الصورة:


تمام الان نبدأ بانشاء Custom Tag Helper
أضف class إلى هذا المجلد باسم BackgroundColorBt.cs. ثم أضف الكود التالي اليه:
 
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement(Attributes = "background-color")]
    public class BackgroundColorBt:TagHelper
    {
        public string BackgroundColor { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
        }
    }
}
لاحظ اننا استخدمنا:
 using Microsoft.AspNetCore.Razor.TagHelpers
حيث يجب استخدام هذا Namespace عن عمل custom tag helper، لاحظ أيضا الكود:
public class BackgroundColorBt:TagHelper
{
}
تم أيضا إضافة خاصية من نوع string باسم BackgroundColor. يقوم ASP.NET Core بفحص جميع خصائص Custom Tag Helper ويعين قيم جميع الخصائص التي يتطابق اسمها مع attribute المطبقة على عنصر HTML. سيتم أيضًا تحويل قيمة attribute إلى نوع الخاصية المحددة في C# class.
ملاحظات:
  • يجب أن يكون اسم attribute بأسلوب html مثل background-color. لا يمكنك تعيين أسماء attribute التي تبدأ بـ -asp أو –data (الكلمات المحجوزه في Net Core.)الخ.
 مثال: 
عند إضافة attribute  التالية "background-color="primary  إلى HTML element. سيتم تعيين قيمة الخاصية BackgroundColor التي هي هنا تساوي primary تلقائيًا HTML element.

  • يحتوي Tag Helper أيضًا على function Process  يقوم بعمل override لوظيفة base class.يعمل هذا ال function  على اضافة CSS class attribute إلى HTML element المستهدف. 
حيث تطبيق المتغير BackgroundColor على CSS class باستخدام الكود 
output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
سطر الكود:
[HtmlTargetElement(Attributes = "background-color")]
يعمل على اخبار class أنTag Helper  هذا ينطبق على  أي html element في view بحيث يحتوي على attribute background-color.

تسجيل Custom Tag Helper
الجزء الأخير هو تسجيل Custom Tag Helper في ملف ViewImports.cshtml_
انتقل الى ملف ViewImports.cshtml_ ثم أضف الكود التالي:
@addTagHelper StudentsAcademy.TagHelpers.*, StudentsAcademy
الكود أعلاه عبارة عن جزئيين:
 الجزء الأول
هو اسم namespace التي أنشأناها وهي
 StudentsAcademy.TagHelpers.* 
متبوعة بالعلامة (*). هذه العلامة تعني جميع الملفات الموجودة داخل مساحة الاسم
 StudentsAcademy.TagHelpers.
الجزء الثاني
يحدد اسم assembly وهنا سيكون اسم المشروع، StudentsAcademy.

استخدام Custom Tag Helper
انتقل الآن إلى AllCourses View ثم أضف الكود التالي:

@model IEnumerable<CoursesModel>
@{ ViewBag.Title = "All Courses";}
<h2>All Reservations</h2>
<a  asp-action="AddCourse"  class="btn btn-success" >Add New Course</a>
<button type="submit" background-color="danger">Add New Course</button>
<table class="table table-sm table-striped table-bordered m-2">
    <thead><tr><th>CourseNumber</th><th>CourseName</th><th>Course Description</th><th>Course Price</th></tr></thead>
    <tbody>
        @if (Model != null)
        {
            foreach (var r in Model)
            {
                <tr>
                    <td>@r.CourseNumber</td>
                    <td>@r.CourseName</td>
                    <td>@r.CourseDescription</td>
                    <td>@r.Price</td>
                </tr>
            }
        }
    </tbody>
</table>
قم بتشغيل التطبيق وانتقل إلى
https://localhost:44382/courses/allcourses
ستلاحظ انه تم إضافة زر جديد باللون الأحمر حيث تم تطبيق الخصائص من بناء على Tag Helper الذي أنشأناه سابقا.




في الكود أعلاه اضفنا "background-color="danger وهي خاصية يتم تطبيقها على button. حيث تم تعريف هذه الخاصية في ملف BackgroundColorBt، تستقبل هذه الخاصية متغير اللون تلقائيا الذي هو هنا danger ويتم تطبيقه على attribute. 
بعد ذلك وفي ملف BackgroundColorBt، يقوم Process function على إضافة CSS class  التالي
btn btn-{property value}
على html element  والتي تصبح btn btn-danger.
النتيجة التي حصلنا عليها هي btn btn-danger  والتي هي عبارة عن bootstrap class لإعطاء خلفية حمراء اللون وبالتالي يصبح الزر أحمر.
فهم Process Function Parameters
بالرجوع الى Process function الذي تم انشاءه سابقا ستلاحظ انه يحتوي على 2 parameters من النوع:
  1. TagHelperContext
  2. TagHelperOutput
الكود الخاص بهذا function
 
public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
        }

TagHelperContext class

TagHelperContext class هو first parameter لهذه ()Process .
فيTag Helpers يتم تلقي المعلومات حول element  الذي سيتم عمل transforming له من خلال TagHelperContext class.  
خصائص TagHelperContext class مذكورة أدناه:



Description
Name
يحتوي على قاموس للقراءة فقط لجميع attributes المطبقة على العنصر الجاري عمل transformed له.
AllAttributes
يحتوي على unique identifier للعنصر الجاري عمل transformed له.
UniqueId
تقوم بإرجاع قاموس يستخدم للتنسيق بين tag helpers ،
Items
TagHelperOutput class
TagHelperOutput class هو  2nd parameter لهذه ()Process . 
يحتوي class object هذا على HTML element المراد تحويله 

خصائص هذا class .

Description
Name
الحصول على أو تعيين اسم العلامة ل output element.
TagName
تقوم بإرجاع قاموس يحتوي على جميع attributes ل output element.
Attributes
يستخدم لتعيين محتويات output element.
Content
يستخدم لإدراج Elements قبل output element
PreElement
يستخدم لإدراج Elements بعد output element
PostElement
يستخدم لإدراج محتويات في output element قبل أي محتوى موجود.
PreContent
يستخدم لإدراج محتويات في output element بعد أي محتوى موجود.
PostContent
TagHelperOutput class methods.

Description
Name
يستخدم لاستبعاد عنصر exclude an element
SupressOuput()

أدارة نطاق Managing the Scope of a Tag Helper
المقصود ب إدارة نطاق Tag Helper، هو التحكم في element الذي ينطبق عليه tag helper. يتم ذلك باستخدام [()HtmlTargetElement] attribute مع وصف قيودها.
خصائص HtmlTargetElement التي يمكنك تطبيقها هي:


Description
Name

تنص هذه الخاصية على أنه يجب تطبيق tag helper على elements التي لديها مجموعة من attributes.

لنأخذ مثال على ذلك:

background-color-*

في هذه الحالة فان tag helper سيتطابق مع attribute التي تبدأ ب background-color- وما بعدها مثل 

background-color-white 
background-color-black

Attributes
تنص هذه الخاصية على أنه يجب تطبيق tag helper على elements الموجودة داخل element من نوع معين.
ParentTag

تنص هذه الخاصية على أنه يجب تطبيق tag helper على العناصر التي لها tag structure تتوافق مع قيمة معينة.

تُعطى هذه القيمة من TagStructure enumeration الذي يعرّف " Unspecified’" و "NormalOrSelf Closing" و "WithoutEndTag"

TagStructure

تمام واكيد بنقدر نضيف المزيد من القيود،
سنقوم الان بإضافة قيد الى الكود السابق بحيث ينطبق tag helper فقط على أي button element في View يحتوي على background-color attribute.
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("button", Attributes = "background-color")]
    public class BackgroundColorBt:TagHelper
    {
        public string BackgroundColor { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
        }
    }
}

في الكود التالي يتم اضافة قيد آخر لتطبيق tag helper  على أي button  في View يحتوي على background-color attribute ويجب ايضا ان يكون هذا button  تم تضمينه في parent ل form.

using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("button", Attributes = "background-color", ParentTag = "form")]
    public class BackgroundColorBt:TagHelper
    {
        public string BackgroundColor { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
        }
    }
}
يمكنك استخدام أكثر من [()HtmlTargetElement] attribute في tag helper.
الكود التالي نقوم بتطبيق Tag Helper  علىbutton  و anchor  التي سيكون لها background-color attribute.

using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("button", Attributes = "background-color", ParentTag = "form")]
    [HtmlTargetElement("a", Attributes = "background-color")]
    [HtmlTargetElement("h1", Attributes = "background-color")]
    public class BackgroundColorBt:TagHelper
    {
        public string BackgroundColor { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
        }
    }
}


استخدام Tag Helpers مع Custom HTML Elements
يمكن انشاء Custom HTML Elements  خاصة بنا كما في Tag Helpers ، ويمكن ايضا تطبيق Tag Helpers على هذه Custom HTML Elements من أجل تحويلها إلى أي عنصر HTML تريده.
على سبيل المثال ، إذا أضفت الكود التالي داخل صفحة HTML او View :
<Mybutton type="submit" background-color="danger" />
لن يتم عمل rendered  لهذا الكود في View  لأنه لا يوجد tag  باسم  Mybutton في HTML.
يمكن استخدام Tag Helpers لانشاء مثل هذا tag  وفي حال انشاءه يسمى هذا ال tag ب Custom HTML Elements
تمام نبدأ بانشاء Custom HTML Elements
انتقل الى مجلد TagHelpers الذي انشاناه سابقا ثم اضف ملف class باسم MyButtonTH.cs ثم اضف الكود التالي

using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("Mybutton")]
    public class MyButtonTH : TagHelper
    {
        public string Type { get; set; } = "Submit";
        public string BackgroundColor { get; set; } = "primary";
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "button";
            output.TagMode = TagMode.StartTagAndEndTag;
            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");
            output.Attributes.SetAttribute("type", Type);
            output.Content.SetContent("Click to Add Record");
        }
    }
}
شرح الكود 

الكود [HtmlTargetElement("Mybutton")] يعني ان element المطلوب سيكون من نوع Mybutton
تم اضافة متغيرين من نوع string هما :
  • Type تحديد طبيعة هذا element انه من نوع Submit
  • BackgroundColor تحديد خاصية هذا element بلون خلفيه يساوي primary
ثم داخل function تم استخدام مجموعة من الخصائص هي : 



الخاصية
الوصف
TagNam
تعمل على جعل  Mybutton من نوع  html button element. حيث تم اعطاءها القيمة button
TagMode
تستخدم ايضا لتحديد أن هذا العنصر سيكتب باستخدام علامات البداية والنهاية.
()SetAttribute 
تستخدم لإضافة class attribute وهنا تم تحديد هذا class بانه من نوع btn btn-{BackgroundColor} حيث يتم ارسال القيمة BackgroundColor له من المتغير BackgroundColor
()SetAttribute
تستخدم لإضافة Type attribute ويتم ارسال قيمة هذا Type من المغير Type
()SetContent
لاضافة اسم او وصف إلى هذا button.

انتقل الى View المسمى AllCourses ثم اضف الكود التالي 
الآن في طريقة Create View الخاصة بك ، استبدل رمز button  بالكود أدناه لعنصر html المخصص.
<Mybutton type="submit" background-color="danger" />
لن نحتاج الى تسجيل هذا Tag Helper لاننا قمنا بذلك سابقا حيث ان مكان هذا tag Helper هو نفس المكان السابق 
كود التسجيل السابق هو : 
@addTagHelper StudentsAcademy.TagHelpers.*, StudentsAcademy
قم بتشغيل التطبيق واكيد بتكون نتيجة التنفيذ كما في الصورة : 



استخدام Appending and Prepending لعناصر Elements في Tag Helper 
 The PreElement and PostElement properties of the TagHelperOutput class is used to add elements before and after the output element.
To help understand them, create a new file inside the CustomTagHelpers folder and name it PrePostElementTH.cs. Add the following code to it:
يتم استخدام خصائص PreElement و PostElement  في  TagHelperOutput class لإضافة elements قبل وبعد  output element.
لفهها بشكل افضل سنقوم بتطبيق مثال على ذلك. 
انتقل الى مجلد  TagHelpers ثم اضف ملف class جديد باسم PrePostElementTH.cs. أضف إليه الكود التالي:
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("div", Attributes = "pre-post")]
    public class PrePostElementTH : TagHelper
    {
        public bool AddHeader { get; set; } = true;
        public bool AddFooter { get; set; } = true;
        public string PrePost { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.SetAttribute("class", "m-1 p-1");
            TagBuilder title = new TagBuilder("h1");
            title.InnerHtml.Append(PrePost);

            TagBuilder container = new TagBuilder("div");
            container.Attributes["class"] = "bg-info m-1 p-1";
            container.InnerHtml.AppendHtml(title);

            if (AddHeader)
                output.PreElement.SetHtmlContent(container);
            if (AddFooter)
                output.PostElement.SetHtmlContent(container);

        }
    }
}

شرح الكود : 
الكود
 [HtmlTargetElement("div", Attributes = "pre-post")] 
يعمل على تطبيق Attributes  من نوع pre-post على div element . 
يستخدم tag helper الخصائص PreElement  و PostElement  لاضافة header and a footer ل element المحيط ب output element
استخدمنا TagBuilder class من Microsoft.AspNetCore.Mvc.Rendering namespace    الذي يعمل على انشاء elements التالية : 
  • h1 - وهو موجود داخل div element
  • div - الذي يحتوي على h1 element. وتم ايضا اضافة Attributes الى div element  هذا من نوع Bootstrap classes  لغرض التصميم.
تم اضافة متغيرين من نوع bool  وهما optional bool attributes بالاسماء التالية: 
 add-header and add-footer 
يتم استخدامها لتحديد ما إذا كان سيتم استبعاد header أو footer ، حيث ان الوضع الطبيعي هو تضمين كل من header and footer.
الخطوة الأخيرة
 هي تطبيق tag helper هذا في صفحة layout_.
لذا انتقل الى صفحة "Layout.cshtml_" ثم اضف الكود التالي:
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
          asp-fallback-href-include="/lib/bootstrap/**/*.min.css"
          asp-fallback-href-exclude="**/*reboot*,**/*grid*"
          asp-fallback-test-class="btn"
          asp-fallback-test-property="display"
          asp-fallback-test-value="inline-block"
          rel="stylesheet" />
    <link rel="stylesheet" asp-href-include="/lib/bootstrap/**/*min.css"
          asp-href-exclude="**/*reboot*,**/*grid*" />
    <script asp-src-include="lib/bootstrap/**/*.js" asp-src-exclude="**.slim.*,**.min.*,**.bundle.*" asp-append-version="true"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"
            asp-fallback-src-include="/lib/jquery/**/*.js"
            asp-fallback-src-exclude="**.slim.**"
            asp-fallback-test="window.jQuery">
    </script>
</head>
<body>
    <div class="container-fluid">
        <div class="bg-info text-warning">
            <cache expires-sliding="@TimeSpan.FromSeconds(10)" vary-by="@ViewContext.RouteData.Values["action"]">
                Date Time: @DateTime.Now.ToString("HH:mm:ss")
            </cache>
        </div>
        <div pre-post="Tag Helpers">
            @RenderBody()
        </div>
        </div>
</body>
</html>

قم بتشغيل التطبيق بالانتقال الى الرابط : 
https://localhost:44382/home/index
ستكون نتيجة التنفيذ كما في الصورة : 



استخدام Appending and Prepending Contents مع Tag Helper
يوفر ASP.Net Core  خاصيتان هما - PreContent و PostContent حيث يتم استخدامهما لإدراج محتويات داخل output element.
  • PreContent : يتم إدراج المحتويات قبل جميع المحتويات الموجودة.
  • PostContent: يتم إدراج المحتويات بعد كل المحتويات الموجودة.
لفهم ذلك شكل افضل سنقوم بانشاء Tag Helper  لاضافة contents
انتقل إلى مجلد TagHelpers ثم أضف ملف class باسم  PrePostContentTH.cs ثم اضف الكود التالي:

using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement("td", Attributes = "underline")]
    public class PrePostContentTH : TagHelper
    {
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.PreContent.SetHtmlContent("<u>");
            output.PostContent.SetHtmlContent("</u>");
        }
    }
}
هنا في هذا tag helper نقوم بالتعامل مع td element  في الجداول حيث تم اضافة attribute تحتوي على underline 
في Function  نقوم بإدراج u html element حوله.
انتقل الى All Courses View ثم قم بتعديل الكود الى الاتي: 
حيث اضفنا كلمة underline لكل من CourseNumber و CourseName

@model IEnumerable<CoursesModel>
@{ ViewBag.Title = "All Courses";}
<h2>All Reservations</h2>
<a  asp-action="AddCourse"  class="btn btn-success" >Add New Course</a>
<button type="submit" background-color="danger">Add New Course</button>
<Mybutton type="submit" background-color="danger" />
<table class="table table-sm table-striped table-bordered m-2">
    <thead><tr><th>CourseNumber</th><th>CourseName</th><th>Course Description</th><th>Course Price</th></tr></thead>
    <tbody>
        @if (Model != null)
        {
            foreach (var r in Model)
            {
                <tr>
                    <td underline>@r.CourseNumber</td>
                    <td underline>@r.CourseName</td>
                    <td >@r.CourseDescription</td>
                    <td >@r.Price</td>
                </tr>
            }
        }
    </tbody>
</table>
تشغيل التطبيق 
قم بتشغيل التطبيق بالانتقال الى الرابط: 
https://localhost:44382/courses/allcourses
ستكون نتيجة التنفيذ كما في الصورة :



 استخدام خصائص TagHelperContext.Items للتنسيق بين Tag Helpers
يوفر ASP.NET Core خاصية تسمى TagHelperContext.Items تعلب دورا مهما في التنسيق بين tag helpers التي تعمل على elements وتلك التي تعمل على الاحفاد descendants.
لفهم هذه الخاصية،
انتقل الى مجلد TagHelpers ومن ثم قم بإنشاء ملف class جديد يسمى CoordinateItemsTagHelpers.cs  ثم اضف الكود التالي إليه:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace StudentsAcademy.TagHelpers
{
  
        [HtmlTargetElement("div", Attributes = "theme")]
        public class ParentThemeTH : TagHelper
        {
            public string Theme { get; set; }
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                context.Items["theme"] = Theme;
            }
        }
        [HtmlTargetElement("button", ParentTag = "div")]
        [HtmlTargetElement("a", ParentTag = "div")]
        public class ChildThemeTH : TagHelper
        {
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                if (context.Items.ContainsKey("theme"))
                    output.Attributes.SetAttribute("class", $"btn btn-{context.Items["theme"]}");
            }
        }
    
}

في هذا Class تم إضافة 2 tag helpers وهما.
  • ParentThemeTH : هذا ال Function يعمل على div elements في View التي لها theme attribute. حيث يقوم بإضافة القيمة theme  من المتغير  public string Theme { get; set; } إلى قاموس العناصر Items dictionary  بحيث تكون متاحة في هذا tag helpers ، ثم يتم تطبيقها على  العناصر الموجودة داخل div element هذا.
  • ChildThemeTH: يعمل على tag التالية buttons  و anchor بشرط ان تكون موجودة داخل div element.  يحصل tag helper هذا على القيم (المخزنة بواسطة ParentThemeTH) من قاموس العناصر. ثم يقوم بتعيين bootstrap CSS على  buttons  و anchors.
اختبار هذه الوظيفة
انتقل الى AddCourse View ثم أضف الكود التالي (تم إضافة لون مختلف للكود المضاف):
@model CoursesModel
@{
    ViewData["Title"] = "Add New Course";
}
<h2>Add New Course</h2>
<form method="post" asp-controller="Courses" asp-action="AddCourse" asp-antiforgery="true">
    <div class="form-group">
        <label asp-for="CourseName">Course Name:</label>
        <input class="form-control" asp-for="CourseName" />
    </div>
    <div class="form-group">
        <label asp-for="CourseDescription">Course Description:</label>
        <input class="form-control" asp-for="CourseDescription" />
    </div>
    <div class="form-group">
        <label asp-for="Price">Price:</label>
        <input class="form-control" asp-for="Price" />
    </div>
    <div class="form-group">
        <label asp-for="Capacity">Capacity:</label>
        <select class="form-control" asp-for="Capacity">
            <option disabled selected value="">Select Capacity</option>
            <option>25</option>
            <option>30</option>
            <option>35</option>
            <option>40</option>
        </select>
    </div>
    <div theme="primary">
        <button type="submit">Add</button>
        <a href="/Home/Index">Cancel</a>
    </div>
    @*<button type="submit" class="btn btn-primary">Add</button>*@
</form>
قم بتشغيل التطبيق بالانتقال الى الرابط:




قم بتغير قيمة theme الى success مثلا ثم قم بتشغيل التطبيق ستلاحظ ان اللون أصبح مختلف
<div theme="success">
        <button type="submit">Add</button>
        <a href="/Home/Index">Cancel</a>
    </div>

كما تلاحظ ان هذا Tag Helper أضاف Them الى جميع ال Tag الموجودة داخل div.

الحصول على View Context Data في Tag Helpers
داخل أي View  يوجد Context Data والتي تحتوي على routing data, ViewData, ViewBag, TempData, ModelState, current HTTP request,  الخ. 
يمكن من داخل tag helpers، الحصول على هذه التفاصيل ل view 
للحصول على View Content Data  
انتقل الى مجلد TagHelpers ثم أضف ملف class باسم ContentDataTH  ثم أضف الكود التالي اليها 
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContextData { get; set; }
تشير ViewContext attribute إلى أنه يجب تعيين قيمة هذه الخاصية لقيمة كائن ViewContext ، عند إنشاء instance جديد ل class Tag Helper.
تخبر HtmlAttributeNotBound attribute بعدم تعيين قيمة لهذه الخاصية إذا كانت هناك view-context attribute على عنصر HTML للإدخال.
دعنا الآن ننشئ tag helper  يحصل على View Content data. لذا أضف class  تسمى FormTH.cs داخل مجلد CustomTagHelpers. ثم أضف الكود التالي إليها.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
 
namespace TagHelpers.CustomTagHelpers
{
    [HtmlTargetElement("form")]
    public class FormTH : TagHelper
    {
        private IUrlHelperFactory urlHelperFactory;
        public FormTH(IUrlHelperFactory factory)
        {
            urlHelperFactory = factory;
        }
 
        [ViewContext]
        [HtmlAttributeNotBound]
        public ViewContext ViewContextData { get; set; }
 
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContextData);
            output.Attributes.SetAttribute("action", urlHelper.Action(
                ViewContextData.RouteData.Values["action"].ToString(), ViewContextData.RouteData.Values["controller"].ToString()));
        }
    }
}
العمل الرئيسي الذي يقوم به هذا tag helper هو - إنشاء عنوان URL ل form’s action attribute بناءً على routing data، وكذلك يستهدف الوصول الى جميع elements الموجودة فيform . 
داخل Process method ، بنحصل على IUrlHelper object من طريقة ()urlHelperFactory.GetUrlHelper . ويتم استخدام IUrlHelper class لإنشاء عنوان URL استنادًا إلى routing data.
الكود الذي يقوم بهذا الجزء هو:

urlHelper.Action(ViewContextData.RouteData.Values["action"].ToString(), ViewContextData.RouteData.Values["controller"].ToString())
بعد ذلك ، انتقل إلى Create View وقم بتغيير form tag لتحتوي فقط على method attribute كما هو موضح أدناه.
<form method="post">
....
</form>
الآن قم بتشغيل التطبيق وانتقل إلى
 URL - https://localhost:44327/Home/Create
ثم تحقق من النماذج التي تم تشكيلها بواسطة HTML ، والتي ستكون:
<form method="post" action="/Home/Create">
    ....
</form>
يوضح هذا بوضوح أن Tag helper قد أضاف تلقائيًا action attribute بعنوان URL مثالي استنادًا إلى نظام التوجيه routing system.

كيفية قمع / منع Suppressing المخرج Output Element
يوفر ASP.NET CORE طريقة يمكن من خلالها منع تضمين output element  في View،  هذه الطريقة تسمى ()SupressOutput في TagHelperOutput class.
لفهم ذلك بشكل أفضل 
سنقوم بإنشاء tag helper جديد. وظيفة هذا tag helper مساعدة div لطلب action method معينة.
انتقل الى مجلد TagHelpers ثم اضف ملف class جديد باسم SuppressOutputTH.cs، ثم اضف الكود التالي إليه.
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.TagHelpers
{
    [HtmlTargetElement(Attributes = "action-name")]
    public class SuppressOutputTH :TagHelper
    {
        public string ActionName { get; set; }
        [ViewContext]
        [HtmlAttributeNotBound]
        public ViewContext ViewContext { get; set; }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (!ViewContext.RouteData.Values["action"].ToString().Equals(ActionName))
                output.SuppressOutput();
        }
    }
}
يستخدم tag helper  هذا ViewContext للحصول على قيمة action  من routing data. ثم يقارنها بقيمة action-name attribute المتوفرة في div element.
إذا لم تتطابق، فسيتم استدعاء طريقة ()SuppressOutput .
لتطبيق هذا Tag Helper انتقل إلى ملف Layout.cshtml_  وقم بإضافة div جديد كما هو موضح في الكود أدناه:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
          asp-fallback-href-include="/lib/bootstrap/**/*.min.css"
          asp-fallback-href-exclude="**/*reboot*,**/*grid*"
          asp-fallback-test-class="btn"
          asp-fallback-test-property="display"
          asp-fallback-test-value="inline-block"
          rel="stylesheet" />

    <link rel="stylesheet" asp-href-include="/lib/bootstrap/**/*min.css"
          asp-href-exclude="**/*reboot*,**/*grid*" />
    <script asp-src-include="lib/bootstrap/**/*.js" asp-src-exclude="**.slim.*,**.min.*,**.bundle.*" asp-append-version="true"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"
            asp-fallback-src-include="/lib/jquery/**/*.js"
            asp-fallback-src-exclude="**.slim.**"
            asp-fallback-test="window.jQuery">
    </script>
</head>
<body>
    <div class="container-fluid">
        <div class="bg-info text-warning">
            <cache expires-sliding="@TimeSpan.FromSeconds(10)" vary-by="@ViewContext.RouteData.Values["action"]">
                Date Time: @DateTime.Now.ToString("HH:mm:ss")
            </cache>
        </div>
        <div action-name="Index" class="bg-secondary">
            <h2>Welcome To Students Academy</h2>
        </div>
        @*<div pre-post="Tag Helpers">*@
        @RenderBody()
        @*</div>*@
    </div>
</body>
</html>
قم بتشغيل التطبيق بالانتقال الى الرابط:
https://localhost:44382/home/index
ستكون النتيجة:


في هذا View تم عرض div وذلك بسبب تطابق action-name بين هذا View وبين routing data 
انتقل بعدها الى الرابط: 
https://localhost:44382/courses/allcourses
ستكون النتيجة: 



سترى أن div الجديد لن يظهر هنا بسبب عدم تطابق action-name بين هذا View وبين routing data.