اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
پنج دقیقه
Razor دارای قابلیتی با نام Templated Razor Delegates است. همانطور که از نام آن مشخص است، یعنی Razor Template هایی که Delegate هستند. در ادامه این قابلیت را با ذکر چند مثال توضیح خواهیم داد.
مثال اول:
میخواهیم تعدادی تگ li را در خروجی رندر کنیم، این کار را میتوانیم با استفاده از Razor helpers نیز به این صورت انجام دهیم:
@helper ListItem(string content) { <li>@content</li> } <ul> @foreach(var item in Model) { @ListItem(item) } </ul>
@{ Func<dynamic, HelperResult> ListItem = @<li>@item</li>; } <ul> @foreach(var item in Model) { @ListItem(item) } </ul>
برای اینکار از نوع Func استفاده خواهیم کرد. این Delegate یک پارامتر را میپذیرد. این پارامتر میتواند از هر نوعی باشد. در اینجا از نوع dynamic استفاده کردهایم. خروجی این Delegate نیز یک HelperResult است. همانطور که مشاهده میکنید آن را برابر با الگویی که قرار است رندر شود تعیین کردهایم. در اینجا از یک پارامتر ویژه با نام item استفاده شده است. نوع این پارامتر dynamic است؛ یعنی همان مقداری که برای پارامتر ورودی Func انتخاب کردیم. در نتیجه پارامتر ورودی یعنی رشته item جایگزین item@ درون Delegate خواهد شد.
در واقع دو روش فوق خروجی یکسانی را تولید میکنند. برای حالتهایی مانند کار با آرایهها و یا Enumerations بهتر است از روش دوم استفاده کنید؛ از این جهت که نیاز به کد کمتری دارد و نگهداری آن خیلی از روش اول سادهتر است. مثال دوم:
اجازه دهید یک مثال دیگر را بررسی کنیم. به طور مثال معمولاً در یک فایل Layout برای بررسی کردن وجود یک section از کدهای زیر استفاده میکنیم:
<header> @if (IsSectionDefined("Header")) { @RenderSection("Header") } else { <div>Default Content for Header Section</div> } </header>
public static HelperResult RenderSection(this WebViewPage page, string name, Func<dynamic, HelperResult> defaultContent) { if (page.IsSectionDefined(name)) { return page.RenderSection(name); } return defaultContent(null); }
<header> @this.RenderSection("Header", @<div>Default Content for Header Section</div>) </header>
مثال سوم: شبیهسازی کنترل Repeater:
یکی از ویژگیهای جذاب WebForm کنترل Repeater است. توسط این کنترل به سادگی میتوانستیم یکسری داده را نمایش دهیم؛ این کنترل در واقع یک کنترل DataBound و همچنین یک Templated Control است. یعنی در نهایت کنترل کاملی بر روی Markup آن خواهید داشت. برای نمایش هر آیتم خاص داخل لیست میتوانستید از ItemTemplate استفاده کنید. همچنین میتوانستید از AlternatingItemtemplate استفاده کنید. یا اگر میخواستید هر آیتم را با چیزی از یکدیگر جدا کنید، میتوانستید از SeparatorTemplate استفاده کنید. در این مثال میخواهیم همین کنترل را در MVC شبیهسازی کنیم.
به طور مثال ویوی Index ما یک مدل از نوع IEnumerable<string> را دارد:
@model IEnumerable<string> @{ ViewBag.Title = "Test"; }
public ActionResult Index() { var names = new string[] { "Vahid Nasiri", "Masoud Pakdel", ... }; return View(names); }
public static HelperResult Repeater<T>(this HtmlHelper html, IEnumerable<T> items, Func<T, HelperResult> itemTemplate, Func<T, HelperResult> alternatingitemTemplate = null, Func<T, HelperResult> seperatorTemplate = null) { return new HelperResult(writer => { if (!items.Any()) { return; } if (alternatingitemTemplate == null) { alternatingitemTemplate = itemTemplate; } var lastItem = items.Last(); int ii = 0; foreach (var item in items) { var func = ii % 2 == 0 ? itemTemplate : alternatingitemTemplate; func(item).WriteTo(writer); if (seperatorTemplate != null && !item.Equals(lastItem)) { seperatorTemplate(item).WriteTo(writer); } ii++; } }); }
توضیح کدهای فوق:
خوب، همانطور که ملاحظه میکنید متد را به صورت Generic تعریف کردهایم، تا بتواند با انواع نوعها به خوبی کار کند. زیرا ممکن است لیستی از اعداد را داشته باشیم. از آنجائیکه این متد را برای کلاس HtmlHelper مینویسیم، پارامتر اول آن را از این نوع میگیریم. پارامتر دوم آن، آیتمهایی است که میخواهیم نمایش دهیم. پارامترهای بعدی نیز به ترتیب برای ItemTemplate، AlternatingItemtemplate و SeperatorItemTemplate تعریف شدهاند و از نوع Delegate با پارامتر ورودی T و خروجی HelperResult هستند. در داخل متدمان یک HelperResult را برمیگردانیم. این کلاس یک Action را از نوع TextWriter از ورودی میپذیرد. اینکار را با ارائه یک Lambda Expression با نام writer انجام میدهیم. در داخل این Delegate به تمام منطقی که برای نمایش یک آیتم نیاز هست دسترسی داریم. ابتدا بررسی کردهایم که آیا آیتم برای نمایش وجود دارد یا خیر. سپس اگر AlternatingItemtemplate برابر با null بود همان ItemTemplate را در خروجی نمایش خواهیم داد. مورد بعدی دسترسی به آخرین آیتم در Collection است. زیرا بعد از هر آیتم باید یک SeperatorItemTemplate را در خروجی نمایش دهیم. سپس توسط یک حلقه درون آیتمها پیمایش میکنیم و ItemTemplate و AlternatingItemtemplate را توسط متغیر func از یکدیگر تشخیص میدهیم و در نهایت درون ویو به این صورت از متد الحاقی فوق استفاده میکنیم:
@Html.Repeater(Model, @<div>@item</div>, @<p>@item</p>, @<hr/>)
public class Product { public int Id { set; get; } public string Name { set; get; } }
<table> <tr> <td>Id</td> <td>Name</td> </tr> @Html.Repeater(Model, @<tr><td>@item.Id</td><td>@item.Name</td></tr>) </table>