ViewComponents در ASP.net 5 و ASP.net MVC 6

چهارشنبه 4 آذر 1394

یکی از امکانات جدید ASP.net MVC 6 ، خصیصه ViewComponent است. ViewComponent برای جایگزینی ChildActions و بعضی PartialView های اضافی در نظر گرفته شده است.

ViewComponents در ASP.net 5 و ASP.net MVC 6

 یکی از امکانات جدید ASP.net MVC 6 ، خصیصه ViewComponent است.  ViewComponent برای جایگزینی ChildActions و بعضی PartialView های اضافی در نظر گرفته شده است.

به طور سنتی در  ASP.net MVC باید یک مدل سراسری در کنترلر ایجاد می کردیم و آن را به  View  پاس می دادیم ، که به سادگی صفحه ای بر اساس داده هایی از مدل ارائه می شد. و نتیجه این بود که  View نیاز نداشت که صریحا برای هر داده ای درخواست کند.

هرچند در تئوری خوب بنظر می رسد اما در عمل مشکلاتی را به همراه داشت. تعدادی اجزاء قابل استفاده مجدد تقریبا در هر وبسایت وجود دارد (منو، کارت خرید، لیستی از همه انواع، فراداده ها و بسیاری دیگر) که در چند صفحه ظاهر می شوند.  MVC 6 چگونه این مشکل را حل میکند؟.

مشکل اجزاء قابل استفاده مجدد

نیاز به نوشتن و جمع آوری داده های مورد نیاز برای ایجاد این مولفه ها در کنترلرهای متعدد می تواند واقعا دردناک باشد. و اغلب برنامه نویسان باید انواع سازنده های مدل و مدل های ارث بری را ایجاد کنند.

یک راه ساده برای حل این مشکل استفاده از ChildAction ها بود که تنها می توانستند توسط  View فراخوانی شوند. متاسفانه از زمانی که helper ها استاتیک شدند ، نمی توانید وابستگی را به آنها تزریق کنید. بنابراین برای استفاده از از چنین کمکی برای بارگذاری داده های بیشتر نیاز است تا از سرویس جایگزین DependencyResolver استفاده کنید. 

ViewComponent در واقع یک شبکه از هر دو HTMLHelper  و  ChildAction است .

 

مثالی از  ViewComponent

نمونه زیر را در نظر بگیرید:

public class Product
{
    public string Name { get; set; }
 
    public decimal Price { get; set; }
}
 
public interface IProductService
{
    Task<Product[]> GetPromotedProducts();
}
 
public class ProductService : IProductService
{
    public Task<Product[]> GetPromotedProducts()
    {
        //for simplicity data is in memory
        var data = new[]
        {
            new Product
            {
                Name = "Etape: 20 Great Stages from the Modern Tour de France",
                Price = 9.90m
            },
            new Product
            {
                Name = "Anarchy Evolution: Faith, Science and Bad Religion in a World Without God",
                Price = 8.90m
            },
            new Product
            {
                Name = "The Bright Continent: Breaking Rules and Making Changes in Modern Africa",
                Price = 12.50m
            }
        };
        return Task.FromResult(data);
    }
}

 

ممکن است کدهای مشابهی در پروژه خود داشته باشید. تصور کنید نیاز است که لیستی از محصولات را در چندین جای وبسایت نمایش دهید. معمولا در  MVC 5 و قبل از آن می توانستید از  ChildAction  ، HtmlHelper ، یا سازنده مدل پایه ای استفاده کنید. با کمک  ViewComponent  به سادگی می توان یک کلاس  ViewComponent اضافه کرد:

[ViewComponent(Name = "PromotedProducts")]
public class PromotedProductsViewComponent : ViewComponent
{
    private readonly IProductService _productService;
 
    public PromotedProductsViewComponent(IProductService productService)
    {
        _productService = productService;
    }
 
    public async Task<IViewComponentResult> InvokeAsync()
    {
        var products = await _productService.GetPromotedProducts();
        return View(products);
    }
}

این کلاس می تواند از تزریق وابستگی در  ASP.net 5 استفاده کند که بسیار راحت است . می تواند بعضی چیزهای اولیه مانند JSON یا  String را برگرداند، اما می تواند PartialView خود را داشته باشد. در مثال بالا  Partial  در آدرس زیر قرار خواهد داشت.

Views/Shared/Components/{Componentname}/Default.cshtml

Default.cshtml یک Convenion برای نماهای ViewComponent است.

در مثال ما Razor View  ، همان چیزی که از PartialView انتظار می رود را نشان می دهد.

//file: /Views/Shared/Components/PromotedProducts/Default.cshtml
@model IEnumerable<ViewComponentSite.Controllers.Product>
<div class="panel panel-default">
  <div class="panel-heading">Promoted products</div>
 
  <ul class="list-group">
    @foreach (var product in Model)
    {
        <li class="list-group-item">@product.Name - @product.Price</li>
    }
  </ul>
</div>

 

با توجه به قرار دادن این ViewComponent در  یک  Main view مناسب ، ویژگی  Component از Razor Pages را در قالب کد زیر استفاده میکنید.

<div class="col-md-4">
    <h2>Important menu</h2>
    <ul>
        <li>Home</li>
        <li>About</li>
    </ul>
    @await Component.InvokeAsync("PromotedProducts")
</div>

یک نسخه همگام نیز وجود دارد اگر Component  شما Synchronous یا همگام باشد.

 

برخی اطلاعات پایه ای

به منظور شناخته شدن یک کلاس به عنوان  ViewComponent  ، برای این کار باید :

   .   Public

   .    non-abstract  

   .   non generic

   .     با پسوند  viewComponent تمام شود یا با ViewComponentAttribute   طراحی شود.

همانطور که میبینید همانند باقی  MVC 6 به شدت به قراردادها متکی است . برای ایجاد یک ViewComponent لازم نیست از هیچ کلاس خاصی ارث بری کنید یا رابطی پیاده سازی کنید.

از آنجایی که قابلیت های واقعی درون  ViewComponent توسط قراردادها(Covention) تعریف می شوند به شکل یکی از متدهای زیر پیاده سازی می شود .

   public Task<IViewComponentResult> InvokeAsync()
    {
        //do stuff
    }
 
    public IViewComponentResult Invoke()
    {
        //do stuff
    }

فراخوانی کننده پیش فرض  ViewComponent ابتدا به دنبال یک متد همگام می گردد. و اگر پیدا نشد سعی خواهد کرد مجددا نسخه همگام سازی را فراخوانی کند. اگر باز هم پیدا نشد یک خطای زمان اجرا را میبینید. با توجه به نیازهای ذکر شده در بالا یک  ViewComponent معتبر برای یک کلاس در نظر گرفته شد.  هنوز هم ممکن است یک خطای زمان اجرا رخ دهد اگر املا یا نامگذاری غلط برای فراخوانی متد داشته باشید  .

بسته به نیاز شما یک کلاس پایه ای  ViewComponent وجود دارد که می توانید برای ارث بری انتخاب کنید. با استفاده از آن به تمام داده های متنی در  View  مانند اطلاعات کاربر اصلی ، HttpContext در جریان ، ViewData و همه اطلاعاتی که معمولا در  View دارید دسترسی دارید. همچنین می توانید براحتی با فراخوانی متد View() در هنگام بازگشت از  View Component یک  RazorView  به ViewComponent اضافه کنید .

 فرستادن داده ها به  Component  مشکل است پس پارامترهای مربوط را به  signature اضافه کنید. برای مثال :

 public Task<IViewComponentResult> InvokeAsync(string text, Foo bar)
    {
        //do stuff
    }

برای فراخوانی چنین Component از  view  ، از سربار  Component.InvokeAsync استفاده میکنیم.

@await Component.InvokeAsync("MyComponent", "text", new Foo())

 

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید