در نگارشهای پیشین ASP.NET MVC، یک چنین مسائلی را با معرفی Child Actionها
public partial class SidebarMenuController : Controller { const int Min15 = 900; [ChildActionOnly] [OutputCache(Duration = Min15)] public virtual ActionResult Index() { return PartialView("_SidebarMenu"); } }
یک مثال: تهیهی اولین View Component
ساختار یک View Component، بسیار شبیه است به ساختار یک Controller، اما با عملکردی محدود. به همین جهت کار تعریف آن با افزودن یک کلاس سیشارپ شروع میشود و این کلاس را میتوان در پوشهای به نام ViewComponents در ریشهی پروژه قرار داد (اختیاری).
سپس برای نمونه، کلاس ذیل را به این پوشه اضافه کنید:
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Core1RtmEmptyTest.Services; namespace Core1RtmEmptyTest.ViewComponents { public class SiteCopyright : ViewComponent { private readonly IMessagesService _messagesService; public SiteCopyright(IMessagesService messagesService) { _messagesService = messagesService; } public IViewComponentResult Invoke(int numberToTake) { var name = _messagesService.GetSiteName(); return View(viewName: "Default", model: name); } //public async Task<IViewComponentResult> InvokeAsync(int numberToTake) //{ // return View(); //} } }
ساختار کلی یک کلاس ViewComponent شامل دو جزء اصلی است:
الف) از کلاس پایه ViewComponent مشتق میشود. به این ترتیب توسط ASP.NET Core قابل شناسایی خواهد شد.
ب) دارای متد Invoke ایی است که بجای Html.RenderAction در نگارشهای پیشین ASP.NET MVC، قابل فراخوانی است. این متد یک View را باز میگرداند.
ج) در اینجا امکان تعریف نمونهی Async متد Invoke نیز وجود دارد (برای مثال جهت کار با متدهای Async بانک اطلاعاتی).
روش فراخوانی این متدها نیز به این صورت است: ابتدا به دنبال نمونهی async میگردد. اگر یافت شد، همینجا کار خاتمه مییابد. اگر یافت نشد، نمونهی sync یا معمولی آن فراخوانی میشود و اگر این هم یافت نشد، یک استثناء صادر خواهد شد.
د) متد Invoke میتواند دارای پارامترهای دلخواهی نیز باشد و حالت پیش فرض آن بدون پارامتر است.
روش یافتن یک view component توسط ASP.NET Core به این صورت است:
الف) این کلاس باید عمومی بوده و همچنین abstract نباشد.
ب) «یکی» از مشخصههای ذیل را داشته باشد:
1) نامش به ViewComponent ختم شده باشد.
2) از کلاس ViewComponent ارث بری کرده باشد.
3) با ویژگی ViewComponent مزین شده باشد.
نحوه و محل تعریف View یک View Component
پس از تعریف کلاس ViewComponent مورد نظر، اکنون نیاز است View آنرا اضافه کرد. روش یافتن این Viewها توسط ASP.NET Core نیز بر این مبنا است که
الف) اگر این View Component عمومی و سراسری است، باید درون پوشهی shared، پوشهی جدیدی را به نام Components ایجاد کرده و سپس ذیل این پوشه، بر اساس نام کلاس ViewComponent، یک زیر پوشهی دیگر را ایجاد و داخل آن، View مدنظر را اضافه کرد (تصویر ذیل).
/Views/Shared/Components/[NameOfComponent]/Default.cshtml
/Views/[CurrentController]/Components/[NameOfComponent]/Default.cshtml
یک نکته: اگر نام کلاسی به ViewComponent ختم شده بود، نیازی نیست تا ViewComponent را هم در حین ساخت پوشهی آن ذکر کرد.
نحوهی استفادهی از View Component تعریف شده و ارسال پارامتر به آن
و در آخر برای استفادهی از این View Component تعریف شده، به فایل layout برنامه مراجعه کرده و آنرا به نحو ذیل فراخوانی کنید:
<footer> <p>@await Component.InvokeAsync("SiteCopyright", new { numberToTake = 5 })</p> </footer>
یک نکته: متدهای قدیمی Component.Invoke و Component.Renderدر اینجا حذف شدهاند (اگر مقالات پیش از RTM را مطالعه کردید) و روش توصیه شدهی در اینجا، کار با متدهای async است.
تفاوتهای View Components با Child Actions نگارشهای پیشین ASP.NET MVC
پارامترهای یک View Component از طریق یک HTTP Request تامین نمیشوند و همانطور که ملاحظه کردید در همان زمان فراخوانی آنها به صورت مستقیم فراهم خواهند شد. بنابراین مباحث model binding در اینجا دیگر وجود خارجی ندارند. همچنین View Components جزئی از طول عمر یک کنترلر نیستند. بنابراین اکشن فیلترهای مختلف تعریف شده، تاثیری را بر روی آنها نخواهند داشت (این مشکلی بود که با Child Actions در نگارشهای قبلی مشاهده میشد). همچنین View Components به صورت مستقیم از طریق درخواستهای HTTP قابل دسترسی نیستند. به علاوه Child actions قدیمی، از فراخوانیهای async پشتیبانی نمیکنند.
زمانیکه کلاسی از کلاس پایه ViewComponent ارث بری میکند، تنها به این خواص عمومی از درخواست HTTP جاری دسترسی خواهد داشت:
[ViewComponent] public abstract class ViewComponent { protected ViewComponent(); public HttpContext HttpContext { get; } public ModelStateDictionary ModelState { get; } public HttpRequest Request { get; } public RouteData RouteData { get; } public IUrlHelper Url { get; set; } public IPrincipal User { get; } [Dynamic] public dynamic ViewBag { get; } [ViewComponentContext] public ViewComponentContext ViewComponentContext { get; set; } public ViewContext ViewContext { get; } public ViewDataDictionary ViewData { get; } public ICompositeViewEngine ViewEngine { get; set; } //... }
فراخوانی Ajax ایی یک View Component
در ASP.NET Core، یک اکشن متد میتواند خروجی ViewComponent نیز داشته باشد و این تنها روشی است که میتوان یک View Component را از طریق درخواستهای HTTP، مستقیما قابل دسترسی کرد:
public IActionResult AddURLTest() { return ViewComponent("AddURL"); }
$(document).ready (function(){ $("#LoadSignIn").click(function(){ $('#UserControl').load("/Home/AddURLTest"); }); });
امکان بارگذاری View Components از اسمبلیهای دیگر نیز وجود دارد
در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 10 - بررسی تغییرات Viewها» روش دسترسی به Viewهای برنامه را که در اسمبلی آن قرار گرفته بودند، بررسی کردیم. دقیقا همان روش در مورد view components نیز صادق است و کاربرد دارد. جهت یادآوری، این مراحل باید طی شوند:
الف) اسمبلی ثالث حاوی View Componentهای برنامه باید ارجاعاتی را به ASP.NET Core و قابلیتهای Razor آن داشته باشد:
"dependencies": { "NETStandard.Library": "1.6.0", "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview2-final", "type": "build" } }, "tools": { "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final" }
"buildOptions": { "embed": "Views/**/*.cshtml" }
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); //Get a reference to the assembly that contains the view components var assembly = typeof(ViewComponentLibrary.ViewComponents.SimpleViewComponent).GetTypeInfo().Assembly; //Create an EmbeddedFileProvider for that assembly var embeddedFileProvider = new EmbeddedFileProvider(assembly,"ViewComponentLibrary"); //Add the file provider to the Razor view engine services.Configure<RazorViewEngineOptions>(options => { options.FileProviders.Add(embeddedFileProvider); });
[ViewComponent(Name = "ViewComponentLibrary.Simple")] public class SimpleViewComponent : ViewComponent
@await Component.InvokeAsync("ViewComponentLibrary.Simple", new { number = 5 })