۱۳۹۱/۰۱/۱۳

ASP.NET MVC #8


معرفي HTML Helpers

يك HTML Helper تنها يك متد است كه رشته‌اي را بر مي‌گرداند و اين رشته مي‌تواند حاوي هر نوع محتواي دلخواهي باشد. براي مثال مي‌توان از HTML Helpers براي رندر تگ‌هاي HTML، مانند img و input استفاده كرد. يا به كمك HTML Helpers مي‌توان ساختارهاي پيچيده‌تري مانند نمايش ليستي از اطلاعات دريافت شده از بانك اطلاعاتي را پياده سازي كرد. به اين ترتيب حجم كدهاي تكراري توليد رابط كاربري در Viewهاي برنامه‌هاي ASP.NET MVC به شدت كاهش خواهد يافت، به همراه قابليت استفاده مجدد از متدهاي الحاقي HTML Helpers در برنامه‌هاي ديگر.
HTML Helpers در ASP.NET MVC معادل كنترل‌هاي ASP.NET Web forms هستند اما نسبت به آن‌ها بسيار سبك‌تر مي‌باشند؛ براي مثال به همراه ViewState و همچنين Event model نيستند.
ASP.NET MVC به همراه تعدادي متد HTML Helper توكار است و براي دسترسي به آن‌ها شيء Html كه وهله‌اي از كلاس توكار HtmlHelper مي‌باشد، در تمام Viewها قابل استفاده است.


نحوه ايجاد يك HTML Helper سفارشي

از دات نت سه و نيم به بعد امكان توسعه اشياء توكار فريم ورك، به كمك متدهاي الحاقي (extension methods) ميسر شده است. براي نوشتن يك HTML Helper نيز بايد همين شيوه عمل كرد و كلاس HtmlHelper را توسعه داد. در ادامه قصد داريم يك HTML Helper را جهت رندر تگ label در صفحه ايجاد كنيم. براي اين منظور پوشه‌ي جديدي به نام Helper را به پروژه اضافه نمائيد (جهت نظم بيشتر). سپس كلاس زير را به آن اضافه كنيد:

using System;
using System.Web.Mvc;

namespace MvcApplication4.Helpers
{
    public static class LabelExtensions
    {
        public static string MyLabel(this HtmlHelper helper, string target, string text)
        {
            return string.Format("<label for='{0}'>{1}</label>", target, text);
        }
    }
}

همانطور كه ملاحظه مي‌كنيد متد Label به شكل يك متد الحاقي توسعه دهنده كلاس HtmlHelper كه تنها يك رشته را بر مي‌گرداند، تعريف شده است. اكنون براي استفاده از اين متد در View دلخواهي خواهيم داشت:

@using MvcApplication4.Helpers
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@Html.MyLabel("firstName", "First Name:")

ابتدا فضاي نام مرتبط با متد الحاقي بايد پيوست شود و سپس از طريق شيء Html مي‌توان به اين متد الحاقي دسترسي پيدا كرد. اگر برنامه را اجرا كنيد، اين خروجي را مشاهده خواهيم كرد. چرا؟

Index
<label for='firstName'>First Name:</label> 

علت اين است كه Razor، اطلاعات را Html encoded به مرورگر تحويل مي‌دهد. براي تغيير اين رويه بايد اندكي متد الحاقي تعريف شده را تغيير داد:

using System.Web.Mvc;

namespace MvcApplication4.Helpers
{
    public static class LabelExtensions
    {
        public static MvcHtmlString MyLabel(this HtmlHelper helper, string target, string text)
        {
            return MvcHtmlString.Create(string.Format("<label for='{0}'>{1}</label>", target, text));
        }
    }
}

تنها تغيير صورت گرفته، استفاده از MvcHtmlString بجاي string معمولي است تا Razor آن‌را encode نكند.

تعريف HTML Helpers سفارشي به صورت عمومي:

مي‌توان فضاي نام MvcApplication4.Helpers اين مثال را عمومي كرد. يعني بجاي اينكه بخواهيم در هر View  آن‌را ابتدا تعريف كنيم، يكبار آن‌را همانند تعاريف اصلي يك برنامه ASP.NET MVC، عمومي معرفي مي‌كنيم. براي اين منظور فايل web.config موجود در پوشه Views را باز كنيد (و نه فايل web.config قرار گرفته در ريشه اصلي برنامه). سپس فضاي نام مورد نظر را در قسمت namespaces صفحات اضافه نمائيد:

<pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="MvcApplication4.Helpers"/>
      </namespaces>

به اين ترتيب متدهاي الحاقي تعريف شده در فضاي نام MvcApplication4.Helpers،‌ در تمام Viewهاي برنامه در دسترس خواهند بود.

استفاده از كلاس TagBuilder براي توليد HTML Helpers سفارشي:

using System.Web.Mvc;

namespace MvcApplication4.Helpers
{
    public static class LabelExtensions
    {
        public static MvcHtmlString MyNewLabel(this HtmlHelper helper, string target, string text)
        {
            var labelTag = new TagBuilder("label");
            labelTag.MergeAttribute("for", target);
            labelTag.InnerHtml = text;
            return MvcHtmlString.Create(labelTag.ToString());
        }
    }
}

در فضاي نام System.Web.Mvc، كلاسي وجود دارد به نام TagBuilder كه كار توليد تگ‌هاي HTML، مقدار دهي ويژگي‌ها و خواص آن‌ها را بسيار ساده مي‌كند و روش توصيه شده‌اي است براي توليد متدهاي HTML Helper. يك نمونه از كاربرد آن‌را براي بازنويسي متد MyLabel ذكر شده در اينجا ملاحظه مي‌كنيد.
شبيه به همين كلاس، كلاس ديگري به نام HtmlTextWriter در فضاي نام System.Web.UI براي انجام اينگونه كارها وجود دارد.


نوشتن HTML Helpers ويژه، به كمك امكانات Razor

نوع ديگري از اين متدهاي كمكي، Declarative HTML Helpers نام دارند. از اين جهت هم Declarative ناميده شده‌اند كه مستقيما درون فايل‌هاي cshtml يا vbhtml به كمك امكانات Razor قابل تعريف هستند. توليد اين نوع متدهاي كمكي به اين شكل نسبت به مثلا روش TagBuilder ساده‌تر است،‌ چون توسط Razor به سادگي و به نحو طبيعي‌تري مي‌توان تگ‌هاي HTML و كدهاي مورد نظر را با هم تركيب كرد (اين رفتار طبيعي و روان، يكي از اهداف Razor است).
به عنوان مثال، تعاريف همان كلاس‌هاي Product و Products قسمت قبل (قسمت هفتم) را در نظر بگيريد. با همان كنترلر و View ايي كه ذكر شد.
سپس براي تعريف اين نوع خاص از HTML Helpers/Razor Helpers بايد به اين نحو عمل كرد:
الف) در ريشه پروژه يا سايت، پوشه‌ي جديدي به نام App_Code ايجاد كنيد (دقيقا به همين نام. اين پوشه، جزو پوشه‌هاي ويژه ASP.NET است).
ب) بر روي اين پوشه كليك راست كرده و گزينه Add|New Item را انتخاب كنيد.
ج) در صفحه باز شده، MVC 3 Partial Page/Razor را يافته و مثلا نام ProductsList.cshtml را وارد كرده و اين فايل را اضافه كنيد.
د) محتواي اين فايل جديد را به نحو زير تغيير دهيد:

@using MvcApplication4.Models
@helper GetProductsList(List<Product> products)
{
    <ul>
        @foreach (var item in products)
        {
            <li>@item.Name ($@item.Price)</li>
        }
    </ul>
}

در اينجا نحوه تعريف يك helper method مخصوص Razor را مشاهده مي‌كنيد كه با كلمه @helper شروع شده است. مابقي آن هم تركيب آشناي code و markup هستند كه به كمك امكانات Razor به اين شكل روان ميسر شده است.
اكنون اگر Viewايي بخواهد از اين اطلاعات استفاده كند تنها كافي است به نحو زير عمل نمايد:

@model List<MvcApplication4.Models.Product>
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@ProductsList.GetProductsList(@Model)

ابتدا نام فايل ذكر شده بعد نام متد كمكي تعريف شده در آن. Model هم در اينجا به ليستي از محصولات اشاره مي‌كند.
همچنين چون در پوشه app_code قرار گرفته، تمام Viewها به اطلاعات آن دسترسي خواهند داشت. علت هم اين است كه ASP.NET به صورت خودكار محتواي اين پوشه ويژه را همواره كامپايل مي‌كند و در اختيار برنامه قرار مي‌دهد.
به علاوه در اين فايل ProductsList.cshtml، باز هم مي‌توان متدهاي helper ديگري را اضافه كرد و از اين بابت محدوديتي ندارد. همچنين مي‌توان اين متد helper را مستقيما داخل يك View هم تعريف كرد. بديهي است در اين حالت قابليت استفاده مجدد از آن‌را به همراه داشتن Viewهايي تميز و كم حجم، از دست خواهيم داد.

جهت تكميل بحث
Turn your Razor helpers into reusable libraries