view component در mvc
شناسه پست: 1510
بازدید: 1328

چکیده: View Component ها در ASP.NET MVC 6، کامپوننتهایی هستند که قابلیت استفاده مجدد را دارند و به عنوان جایگزین برای Partial View ها و Child Action ها در نسخه های قبلی ASP.NET MVC در نظر گرفته شده اند.

 

ASP.NET Core 1.0 (قبلا به نام ASP.NET 5 شناخته میشد) طراحی مجدد از ASP.NET که در تمام این سالها استفاده میکردیم میباشد. در ورژنهای اولیه Web API ،ASP.NET به عنوان یک فریم ورک جداگانه ارائه شده بود. اما با پیش رفتن به سمت ASP.NET Core 1.0، فریم ورک Web API با ASP.NET MVC ادغام شد که MVC 6 نامیده شد. این باعث بوجود آمدن این مزیت شد که بتوان اپلیکیشنها را با استفاده از HTML و API service ها فقط با یک فریم ورک ایجاد کرد.

چرا ViewComponent ها؟

ASP.NET MVC 5 اجازه میدهد که ما لایه مدلها را بسازیم و سپس از آنها با استفاده از کنترلرها برای نمایش داده ها در View ها استفاده کنیم یا از View ها به آنها دسترسی داشته باشیم اما اگر قصد داشته باشیم که از یک رابط کاربری ویژه در همه View ها استفاده کنیم (به عیارتی یک کامپوننت UI با قابلیت استفاده مجدد) باید چکار کنیم؟ به عنوان مثال ما میخواهیم یک امکان جست و جو (TextBox و دکمه جست و جو) در همه View ها داشته باشیم. در این مورد، در حالی که از MVC 5 استفاده میکنیم، باید یک Partial View جداگانه ایجاد کنیم و آن را بین تمام View ها render نماییم یا باید یک متد HtmlHelper جداگانه برای اداره view rendering ایجاد کنیم. در چنین حالاتی، بهتر است که مطمئن شویم که Partial View یا HtmlHelper مورد نظر برای هر Model کار میکند. از آنجایی که هر View مرتبط به یک اکشن متد در یک کنترلر است، برای scaffold کردن View ما Model را از اکشن متد به View مورد نظر پاس میدهیم. همه partial View/HtmlHelper ها حالا به Model مورد نظر برای View، بایند شده اند. در این حالت، ایجاد Partial View/HtmlHelper غیرمستقل از Model یک چالش به حساب می آید و نگه داری آن کاری هزینه بر است. اینجا یک راه حل ساده تر مورد نیاز است.

View Component ها در ASP.NET Core 1.0 (که قبلا به عنوان ASP.NET 5 شناخته میشد)

در ASP.NET Core 1.0، چند ویژگی جدید از جمله View Component معرفی شده بود. یک View Component شبیه partial view ها میباشد اما در مقایسه با آنها قدرتمندتر میباشند. یک کاربرد اصلی View Component، رندر کردن یک تکه به جای کل response میباشد. این برای Render کردن View به همراه داده ها میتواند مورد استفاده قرار گیرد. برای مثال پنل Login/Logout در همه View ها، Navigation Bar، به روزرسانی های اخیر در داده ها و غیره. یکی دیگر از بهبودهای بزرگ در مورد ChildAction ها این است که  View Component ها این امکان را میدهند که عملیات غیرهمزمان (asynchronous) را روی آنها اجرا کنید.

قسمت ا: ایجاد یک کلاس ViewComponent 

ViewComponent دو قسمت دارد. قسمت اول، کلاس ایجاد شده با مشخصات زیر میباشد:

  • یک کلاس از ViewComponent مشتق شده است.
  • اعمال کردن کلاس ViewComponentAttribute به عنوان attribute روی کلاسی که به عنوان ViewComponent مورد استفاده قرار میگیرد.
  • ایجاد یک کلاسی که نام آن با پسوند ViewComponent ایجاد میشود.
  • کلاسی که به عنوان یک ViewComponent در نظر میگیریم باید public باشد و abstract و generic نباشد.
  • اگر ما میخواهیم با آبجکت data-context کار کنیم (از Entity Framework یا هر نوع مکانیسم دسترسی به داده ها، service، repository.)، سپس با استفاده از تزریق سازنده، میتوانیم وابستگی را برای کلاس ViewComponent  در دسترس قرار دهیم.

قسمت 2: ViewComponent – پیاده سازی

برای پیاده سازی این اپلیکیشن، ما از Visual Studio 2015 و ASP.NET Core 1.0 استفاده میکنیم. لطفا برای درک اینکه چطور یک اپلیکیشن ASP.NET MVC 6 را از ابتدا بسازیم مقاله ساخت اپلیکیشن ASP.NET MVC 6 و Entity Framework 7 با ASP.NET 5 RC را ببینید.

مرحله 1: ما قرار است که اپلیکیشنمان را بر روی اپلیکیشنی که در مقاله ساخت اپلیکیشن ASP.NET MVC 6 و Entity Framework 7 با ASP.NET 5 RC ایجاد کردیم بسازیم. پس کلیه مراحل در این مقاله را بخوانید و سورس پروژه را در آن مقاله دانلود نمایید. اپلیکیشن را extraxt کنید و آن را در Visual Studio 2015 باز نمایید. این اپلیکیشن MVC6_App شامل پوشه های Models, Views و Controllers میباشد. اپلیکیشن را اجرا کنید و نتیجه به صورت زیر نمایش داده خواهد شد:

صفحه اول نمایش در mvc

بر روی لینکهای Categories و Products کلیک کنید که در نتیجه صفحات Index مربوط به هردو نمایش داده خواهد شد.

مرحله 2: از آنجایی که ما در قسمت 1 صحبت کردیم، قصد داریم یک View Component ایجاد نماییم. در پروژه، یک پوشه جدید به نام ViewComponents ایجاد نمایید. در این پوشه، یک کلاس با کدهای زیر ایجاد نمایید:

using Microsoft.AspNet.Mvc;
using MVC6_App.Models;
using System.Linq;
 
namespace MVC6_App.ViewComponents
{
    public class ProductListViewComponent : ViewComponent
    {
        private readonly AppDbContext ctx;
 
        public ProductListViewComponent(AppDbContext c)
        {
            ctx = c;
        }
 
        public IViewComponentResult Invoke(string filter)
        {
            var res = (from p in ctx.Product.ToList()
                       where p.ProductName.StartsWith(filter)
                       select p).ToList();
 
 
            return View(res);
        }
    }
}


کد بالا شامل مشخصات زیر میباشد:

  • کلاس ProductListViewComponent (توجه کنید که پسوند ViewComponent میباشد) از کلاس پایه ViewComponent مشتق شده است.
  • اینجا از اینجکشن در سازنده برای نمونه سازی کلاس AppDbContext استفاده شده است. این کلاس برای دسترسی به داده ها مورد استفاده قرار میگیرد.
  • متد ()Invoke به عنوان یک متد public معرفی شده و در view فراخوانی میشود. این متد میتواند با هر تعداد پارامتر مورد دسترسی قرار بگیرد. در این مورد ما، متد با یک پارامتر string مورد دسترسی قرار میگیرد و همه محصولاتی که با مقدار موجود در این متغیر شروع میشود را فیلتر مینماید. ما میتوانیم متد ()InvokeAsync را برای اجرای asynchronous  داشته باشیم.

مرحله 3: برای استفاده از View Component در Razor view، ما باید مراحل زیر را دنبال نماییم:

  • در زیرپوشه Shared  از پوشه Views، یک پوشه هم نام کلاس ViewComponent  با پسوند ViewComponent ایجاد نمایید. در این مثال ما، نام پوشه ProductList میباشد.
  • در این پوشه، یک Razor View جدید به نام Default.cshtml ایجاد نمایید.

در این View، کد زیر را اضافه نمایید:

@model IEnumerable<MVC6_App.Models.Product>
 
<h3>Max. Sold Products</h3>
<ul>
    @foreach (var prd in Model)
    {
        <li>@prd.ProductName</li>
    }
</ul>


کد بالا از Product به عنوان Model class استفاده مینماید. این بر روی model پیمایش میکند و تمام محصولات را با نام آنها نمایش میدهد.

مرحله 4:  Index.cshtml در زیرپوشه Products از پوشه Views را باز نمایید. ما در این View یک حلقه foreach داریم که بر روی کل محصولات پیمایش میکند. قبل از این حلقه، کد زیر را در View اضافه نمایید:

<div class="alert alert-success">@Component.Invoke("ProductList", "D")</div>

 

Component.Invoke@ سینتکسی برای صدازدن View Component میباشد. اولین پارامتر، نام View Component است. این View Component در مسیر Views < Shared > (پوشه ای که هم نام کلاس View Component است) > Default.cshtml جست و جو میشود. دومین پارامتر، پارامتری است که به متد Invoke  از کلاس ViewComponent پاس داده میشود.

مرحله 5: اپلیکیشن را اجرا نمایید و  Products/Index view را ببینید. نتیجه به صورت زیر نمایش داده خواهد شد:

نتیجه view component در mvc

ViewComponent در بالای View با عنوان Max. Sold Products نمایش داده میشود که تمام محصولاتی که با حرف D شروع میشود را نمایش میدهد.

ما میتوانیم از این ViewComponent در تمام View ها هرجا که به Product Model نیاز داریم استفاده کنیم.

ما میتوانیم ViewComponent  را طوری ایجاد نماییم که برای هر نوع model  (ما از نوع IEnumerable در این مثال خودمان استفاده کردیم.) در اپلیکیشنمان مورد استفاده قرار بگیرد. ما ممکن است چند Index View داشته باشیم و بخواهیم داده های فیلتر شده در Index View را با استفاده از ViewComponent نمایش دهیم. در این حالت بهتر است ما یک ViewComponent بسازیم که محتوای Html  با داده های دلخواه را برگرداند.

مرحله 6: در پوشه ViewComponents، یک کلاس جدید به نام SearchViewComponent اضافه نمایید و کدهی زیر را در آن اضافه نمایید:

using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using MVC6_App.Models;
using System.Collections;
using System.Linq;
using System.Reflection;
 
namespace MVC6_App.ViewComponents
{
    public class SearchViewComponent : ViewComponent
    {
        private readonly AppDbContext ctx;
        public SearchViewComponent(AppDbContext c)
        {
            ctx = c;
        }
 
        public IViewComponentResult Invoke(object [] model, string property, string filter)
        {
            var t = (model[0]).GetType();
 
            PropertyInfo[] properties = t.GetProperties();
 
            IEnumerable Res = null;
 
            foreach (PropertyInfo p in properties)
            {
                if (p.Name == property)
                {
                    Res = from pP in model
                          where ((string)pP.GetType().GetProperty(p.Name).GetValue(pP, null)).StartsWith(filter)
                          select pP;
 
                    break;
                }
            }
 
            string html = "<h3>The Filtered Data</h3><ul>";
            foreach (var item in Res)
            {
              html +=  "<li>" + item.GetType().GetProperty(property).GetValue(item, null) + "</li>";
            }
            html += "</ul>";
 
            return new ContentViewComponentResult(new HtmlString(html));
        }
    }
}


در view component بالا، ما متد Invoke  با پارامترهای زیر را داریم:

  • Object array
  • string property
  • string filter

ما از این ViewComponent  برای بررسی اینکه آیا آرایه مدل شامل property با مقدار پاس داده شده در ورودی میباشد یا خیر. سپس از مقدار درون ورودی filter استفاده مینماییم. ما رکوردها را در آرایه مدل با property مورد نظر که با مقدار درون filter شروع میشود را جست و جو میکنیم.

متد ()Invoke از reflection  برای دسترسی به نوع کلاس آرایه استفاده میکند. با استفاده از reflection، خصوصیات درون object خوانده میشوند. اگر property پاس داده شده در خصوصیات درون object موجود باشد سپس از یک کوئری LINQ استفاده میکند و میتوانیم رکوردها را از مدل آرایه مورد نظر بخوانیم. این نتیجه در متغیر Res ذخیره میشود. سپس این متد یک رشته از Html با تگ ul و li میسازد و داده ها را در آن اضافه مینماید. این متد، رشته Html را با استفاده از آبجکت ContentViewComponentResult  برمیگرداند.

ما برای این ViewComponent ، به پوشه جداگانه نیازی نداریم زیرا این متد Invoke  هم اکنون آبجکت ContentViewComponentResult برمیگرداند.

مرحله 7: درپوشه Products  درون پوشه Views فایل Index.cshtml را باز کنید و کد زیر را زیر جدول در html وارد نمایید:

<div class="alert alert-success">@Component.Invoke("Search", Model.ToArray(), "ProductName", "D")</div>

 

اپلیکیشن را اجرا نمایید و به صفحه Products/Index بروید. نتیجه به صورت زیر نمایش داده خواهد شد:

view component در mvc

همین را در View های دیگر مانند Categories/Index تست کنید.

نتیجه گیری

View Component ها در ASP.NET MVC، نقش مهمی در قابلیت استفاده مجدد UI در بین صفحات دارد. آنها جایگزین مناسبی برای ChildAction و Partial View ها میباشند. یک بهبود قابل توجه روی ChildAction ها این است که View Component ها به شما اجازه این را میدهد که عملیات غیر همزمانی (asynchronous) را بر روی آنها انجام دهید.

برای دانلود سورس برنامه اینجا کلیک نمایید.

نویسنده

امید عباسی
من امید عباسی هستم. سالهاست که در زمینه برنامه نویسی با تکنولوژی دات نت فعالیت میکنم و عاشق این هستم که تجربیات و دانش خودم را در این زمینه با دیگران به اشتراک بزارم. خیلی دوست دارم که نظر و انتقاد خودتون رو در مورد این نوشته برای من بنویسید تا بتونم در آینده، مطالب بهتر و ارزشمندتری را برای شما فراهم کنم. در صورت داشتن هرگونه سوال هم در قسمت دیدگاه ها میتونید با بنده در ارتباط باشید
  1. سلام وقت بخیر
    من یه سوال دارم ایا از viewcomponent ها برای بخش بخش کردن صفحه میتوان استفاده کرد؟ یا راهکار دیگری وجود دارد.
    من در یک صفحه میخوام اگر یکی از موارد درخت رو انتخاب کرد بتواند برای آن زیر شاخه تعریف کند در همان صفحه
    میشه راهنمایی کنید؟
    ممنون

    1. سلام دوست عزیر

      با توجه به اینکه در سوال شما، ابهام وجود داره نمیشه پاسخ صحیحی بهش داد.
      همانطور که در مقاله هم توضیح داده شد، View Component برای ایجاد کامپوننتهای مختلف بر اساس View است که تقریبا شبیه به partialView بوده ولی با امکانات بهتر. به عبارتی شما میتونید چندین viewcomponent داشته باشید و همه آنها را در صفحه خود فراخوانی کنید.

      حالا باز منظورتون از بخش بخش کردن صفحه رو دقیقا متوجه نشدم.

      و منظورتون از درخت، اگر ساختار درختی مربوط به یک مدل است. مثلا مثل مدل دسته بندی ها که ساختار پدر و فرزندی دارد برای ایجاد زیرشاخه در آن، فکرنمیکنم ارتباطی به viewcomponent ها داشته باشه. بلکه شما میتونید با استفاده از برنامه نویسی سمت کلاینت و ajax، یک درخواست برای ثبت شاخه جدید برای درخت مورد نظر بنویسید. طوری که در حین ثبت زیرشاخه، نیازی به post back در برنامه نباشه و بلافاصله زیرشاخه مورد نظر ثبت بشه.

      اگر باز هم ابهام و یا سوالی بود در خدمتم.

      موفق باشید

      1. ابتدا ممنون از پاسخگویی شما
        من در کل می خوام مثلا توی یک صفحه بخش ثبت اطلاعات کاربر وجود داشته باشه و در زیر آن گریدی که اطلاعات کاربران رو نمایش بده اگر هر سطر انتخاب شد اطلاعات کاربر در input های بالا نمایش داده بشه و ویرایش انجام گیرد.
        یا مثلا در یک بخش صفحه همون ساختار درختی پدر و فرزندی موجود باشد و انتخاب که شد در یک input دیگر مقدار نود جدید ثبت بشه
        امیدوارم تونسته باشم درست توضیح بدم
        ممنونم از راهنمایی تون

        1. سلام و درود بر شما

          خب برای همچین موردی، نیازی به viewComponent نیست. ولی به فرض مثال اگر قصد دارید که قسمت نمایش اطلاعات کاربران ثبت شده را در صفحات مختلف نمایش بدید میتونید طبق مقاله، اون رو در یک viewComponent درست کنید و در صفحات مختلف، این ViewComponent رو فراخوانی کنید و نمایشش بدید.
          در قسمت گرید هم میتونید انتخاب سطر و قرار گرفتن اطلاعات مربوطه در کنترلهای بالای گرید رو به راحتی توسط یک کنترلر و اکشن متد handle کنید که نحوه انجام این مورد خارج از مبحث این مقاله است.

          برای اون مبحث نود در درخت هم همونطور که در دیدگاه قبلی گفتم بهتره که با جاوااسکریپت و ajax نوشته بشه.

          موفق باشید