در مطلب «
ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» با مفهوم جدید Tag Helpers و همچنین نحوهی استفادهی از نمونههای پیش فرض و توکار آن در ASP.NET Core آشنا شدیم. در ادامه قصد داریم با نحوهی پیاده سازی نمونههای سفارشی آنها نیز آشنا شویم.
نوشتن یک Tag Helper سفارشی، برای رندر کردن لیستهای بوت استرپی
فرض کنید میخواهیم یک tag helper جدید را جهت رندر کردن لیست بوت استرپی ذیل تهیه کنیم:
<ul class="list-group">
<li class="list-group-item">Item 1</li>
<li class="list-group-item">Item 2</li>
<li class="list-group-item">Item 3</li>
</ul>
برای اینکار یک کتابخانهی جدید را به پروژهی جاری اضافه کرده و سپس وابستگیهای ذیل را نیز به آن اضافه میکنیم. اینها حداقلهایی هستند که جهت دسترسی به امکانات MVC و Tag Helpers، در یک پروژهی مجزای Class library نیاز داریم:
{
"version": "1.0.0-*",
"dependencies": {
"NETStandard.Library": "1.6.0",
"Microsoft.AspNetCore.Http.Extensions": "1.0.0",
"Microsoft.AspNetCore.Mvc.Abstractions": "1.0.1",
"Microsoft.AspNetCore.Mvc.Core": "1.0.1",
"Microsoft.AspNetCore.Mvc.ViewFeatures": "1.0.1",
"Microsoft.AspNetCore.Razor.Runtime": "1.0.0"
},
"frameworks": {
"netstandard1.6": {
"imports": "dnxcore50"
}
}
}
بررسی آناتومی یک کلاس TagHelper
یک کلاس Tag Helper سفارشی، در حالت کلی میتواند شکل زیر را داشته باشد:
namespace Core1RtmEmptyTest.TagHelpers
{
[HtmlTargetElement("list-group")]
public class ListGroupTagHelper : TagHelper
{
[HtmlAttributeName("asp-items")]
public List<string> Items { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
}
}
}
در اینجا نام کلاس، به TagHelper ختم میشود و همچنین این کلاس از کلاس پایهی TagHelper ارث بری میکند. ذکر HtmlTargetElement الزامی بوده و در صورت عدم تعریف آن، TagHelper تعریف شده توسط ASP.NET Core شناسایی و بارگذاری نخواهد شد.
توسط HtmlTargetElement نام نهایی تگ مرتبط با TagHelper سفارشی را تعریف و سفارشی سازی کردهایم. در این حالت این TagHelper جدید در Viewهای برنامه، توسط تگ ذیل شنایی میشود (بجای نام پیش فرض کلاس):
<list-group></list-group>
همچنین در اینجا، یک خاصیت عمومی نیز تعریف شدهاست. تمام خواص عمومی تعریف شدهی در اینجا به صورت ویژگیهایی در تگ نهایی TagHelper قابل دسترسی و مقدار دهی خواهند بود:
<list-group asp-items="Model.Items"></list-group>
برای لغو این حالت میتوان از ویژگی HtmlAttributeNotBound استفاده کرد.
برای اینکه نام این ویژگی را نیز بتوانیم سفارشی سازی کنیم، میتوان از ویژگی HtmlAttributeName استفاده کرد. در صورت عدم ذکر آن، از نام پیش فرض این خاصیت عمومی جهت تعریف ویژگیهای تگ نهایی استفاده میگردد.
عملیات نهایی افزودن تگهای HTML، به View برنامه، در متد Process انجام میشود. در اینجا توسط متدهایی مانند output.Content.AppendHtml میتوان خروجی دلخواهی را به صفحه اضافه کرد.
تکمیل کدهای Tag Helper سفارشی رندر کردن لیستهای بوت استرپی
پس از آشنایی با ساختار کلی یک کلاس TagHelper، اکنون میتوان کدهای آن را به نحو ذیل تکمیل کرد:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Core1RtmEmptyTest.TagHelpers
{
[HtmlTargetElement("list-group")]
public class ListGroupTagHelper : TagHelper
{
[HtmlAttributeName("asp-items")]
public List<string> Items { get; set; }
protected HttpRequest Request => ViewContext.HttpContext.Request;
[ViewContext, HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
if (Items == null)
{
throw new InvalidOperationException($"{nameof(Items)} must be provided");
}
output.TagName = "ul";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add("class", "list-group");
foreach (var item in Items)
{
TagBuilder itemBuilder = new TagBuilder("li");
itemBuilder.AddCssClass("list-group-item");
itemBuilder.InnerHtml.Append(item);
output.Content.AppendHtml(itemBuilder);
}
}
}
}
توضیحات:
- چون میخواهیم تگ نهایی آن، list-group نام داشته باشد، آنرا توسط ویژگی HtmlTargetElement به صورت صریحی مشخص کردهایم.
- همچنین علاقمندیم تا ویژگی دریافت لیست آیتمها، نامی معادل asp-items داشته باشد. بنابراین آنرا نیز توسط ویژگی HtmlAttributeName، دقیقا مشخص کردهایم.
- در این کلاس، یک خاصیت اضافهی ViewContext را نیز مشاهده میکنید. ویژگی ViewContext اعمالی به آن، سبب خواهد شد تا اطلاعات درخواست جاری، به این خاصیت عمومی، به صورت خودکار تزریق شود. بنابراین اگر نیاز به اطلاعاتی مانند Request جاری دارید، شیء ViewContext.HttpContext.Request، این مقادیر را در اختیار شما قرار میدهد. به علاوه اگر دقت کرده باشید، این خاصیت با ویژگی HtmlAttributeNotBound مزین شدهاست. از این جهت که نمیخواهیم این خاصیت عمومی، در لیست ویژگیهای تگ نهایی TagHelper در حال تهیه، ظاهر شود.
- پس از آن کاری که انجام شده، تکمیل متد Process است. در اینجا توسط output.TagName مشخص میکنیم که TagHelper جاری، در بین تگهای ul قرار گیرد (مفهوم TagMode.StartTagAndEndTag ذکر شده) و همچنین این تگ محصور کننده دارای کلاس list-group بوت استرپ نیز خواهد بود.
- سپس بر روی لیست آیتمهای دریافت شده، یک حلقه را تشکیل داده و به کمک TagBuilder، تگهای li داخل ul برونی را تکمیل میکنیم. این TagBuilder در نهایت توسط متد output.Content.AppendHtml به View برنامه اضافه خواهد شد. در اینجا، متد Append هم وجود دارد. اگر از آن استفاده شود، خروجی نهایی HTML Encoded خواهد بود.
ثبت و استفادهی از TagHelper سفارشی تهیه شده
پس از تعریف TagHelper سفارشی فوق، ابتدا ارجاعی از اسمبلی آنرا به پروژهی جاری اضافه میکنیم و سپس به فایل ViewImports.cshtml_ برنامه مراجعه و یک سطر ذیل را به آن اضافه میکنیم:
@addTagHelper *,Core1RtmEmptyTest.TagHelpers
در اینجا عبارت Core1RtmEmptyTest.TagHelpers همان نام فضای نام اصلی پروژهی Class library دربرگیرندهی TagHelper سفارشی است.
اکنون که این TagHelper در Viewهای برنامه قابل شناسایی است، روش افزودن آن، بر اساس همان سفارشی سازیهایی است که انجام دادیم: