Trong phát triển ứng dụng web với ASP.NET Core MVC, Partial View (hay còn gọi là view con) là một công cụ mạnh mẽ giúp tổ chức và tái sử dụng các phần tử giao diện người dùng. Về bản chất, Partial View là một tệp .cshtml được kết xuất bên trong một view khác, và đầu ra HTML của nó sẽ được nhúng vào vị trí gọi trong view cha.
Khi nào nên sử dụng Partial View?
Việc sử dụng Partial View mang lại nhiều lợi ích, đặc biệt là khi quản lý các dự án web lớn và phức tạp:
- Tái sử dụng mã giao diện: Thay vì viết lại cùng một đoạn HTML ở nhiều nơi, bạn có thể đóng gói nó vào một Partial View và sử dụng lại. Ví dụ điển hình là các thành phần như phần bình luận, thông tin tác giả, hoặc một widget nhỏ.
- Phân tách trách nhiệm: Đối với các trang phức tạp chứa nhiều thành phần logic riêng biệt, việc chia nhỏ chúng thành các Partial View giúp dễ quản lý, đọc hiểu và bảo trì hơn. Mỗi Partial View có thể tập trung vào việc hiển thị một phần cụ thể của dữ liệu.
- Cải thiện cấu trúc: Giúp view chính gọn gàng hơn, dễ dàng theo dõi bố cục tổng thể của trang.
Một điểm khác biệt quan trọng cần lưu ý là Partial View không chạy tệp _ViewStart.cshtml. Điều này có nghĩa là các thiết lập bố cục và các khối mã khởi tạo chung trong _ViewStart.cshtml sẽ không được áp dụng cho Partial View, giúp chúng có tính độc lập cao hơn và chỉ tập trung vào nội dung của mình.
Các phương thức gọi Partial View
Có nhiều phương thức để gọi và nhúng một Partial View vào view chính:
-
Html.PartialAsync(): Đây là phương thức được khuyến nghị khi làm việc với Partial View, đặc biệt là khi Partial View có thể chứa các hoạt động bất đồng bộ. Nó trả về một đối tượngIHtmlContent, cho phép bạn nhúng kết quả vào markup của view:@await Html.PartialAsync("_AuthorInfoDisplay") -
Html.RenderPartialAsync(): Phương thức này trực tiếp ghi đầu ra HTML của Partial View vào luồng phản hồi, thay vì trả về một đối tượng. Do đó, nó phải được gọi bên trong một khối mã Razor (@{ ... }):@{ await Html.RenderPartialAsync("_AuthorInfoDisplay"); }Phương thức
RenderPartialAsyncthường hiệu quả hơn về mặt hiệu suất vì nó không cần tạo chuỗi trung gian, nhưng đòi hỏi cú pháp gọi khác. -
Html.Partial()vàHtml.RenderPartial(): Đây là các phiên bản đồng bộ của các phương thức trên. Mặc dù vẫn hoạt động, chúng không được khuyến nghị sử dụng trong môi trường ASP.NET Core hiện đại do có thể gây tắc nghẽn luồng xử lý chính khi có các thao tác I/O.@Html.Partial("_AuthorInfoDisplay")
Cách View Engine tìm Partial View
Khi bạn gọi một Partial View bằng tên, ASP.NET Core MVC sẽ áp dụng một logic tìm kiếm cụ thể để xác định vị trí của tệp .cshtml:
// 1. Tìm trong thư mục hiện tại của view gọi, sau đó trong thư mục 'Shared'.
// Ví dụ: Nếu view gọi nằm ở /Views/Blog/, nó sẽ tìm ở /Views/Blog/_SectionContent.cshtml,
// rồi đến /Views/Shared/_SectionContent.cshtml.
@await Html.PartialAsync("_SectionContent")
// 2. Chỉ định tên file đầy đủ trong thư mục hiện tại.
@await Html.PartialAsync("_SectionContent.cshtml")
// 3. Sử dụng đường dẫn tuyệt đối từ gốc ứng dụng (bắt đầu bằng '~/' hoặc '/').
@await Html.PartialAsync("~/Views/Shared/Components/_AuthorInfoDisplay.cshtml")
// 4. Sử dụng đường dẫn tương đối.
@await Html.PartialAsync("../User/_UserProfileCard.cshtml")
Các Partial View có thể gọi lồng nhau (chaining), nghĩa là một Partial View có thể chứa một lời gọi đến Partial View khác, miễn là không tạo ra vòng lặp vô hạn.
Truyền dữ liệu đến Partial View
Khi một Partial View được kết xuất, nó sẽ nhận một bản sao của đối tượng ViewDataDictionary từ view cha. Mọi thay đổi trong ViewData của Partial View sẽ không ảnh hưởng đến ViewData của view cha sau khi Partial View hoàn tất việc kết xuất.
Bạn có thể truyền dữ liệu cụ thể đến Partial View thông qua các cách sau:
-
Truyền Model: Bạn có thể truyền một đối tượng model cụ thể tới Partial View. Partial View sau đó sẽ định nghĩa kiểu model của nó bằng
@model MySpecificModel.@await Html.PartialAsync("_ItemDetails", itemData) -
Truyền
ViewDataDictionarytùy chỉnh: Tạo một instance mới củaViewDataDictionaryvà truyền nó vào.@{ var customViewData = new ViewDataDictionary(ViewData) { {"Message", "Thông báo từ Partial!"} }; } @await Html.PartialAsync("_CustomMessage", null, customViewData) -
Truyền cả Model và
ViewDataDictionary:@await Html.PartialAsync("_CombinedDisplay", itemData, customViewData)
Ví dụ thực tế
Hãy xem xét một ví dụ minh họa cách sử dụng Partial View để hiển thị nội dung một bài viết blog.
1. Định nghĩa Models
namespace App.Web.Models
{
public class BlogPost
{
public BlogPost()
{
ContentSections = new List<BlogPostSection>();
}
public string AuthorDisplayName { get; set; }
public List<BlogPostSection> ContentSections { get; set; }
}
public class BlogPostSection
{
public string HeaderText { get; set; }
public string BodyContent { get; set; }
}
}
2. Controller Action
using Microsoft.AspNetCore.Mvc;
using App.Web.Models;
namespace App.Web.Controllers
{
public class BlogController : Controller
{
public IActionResult ViewPost()
{
var blogPost = new BlogPost
{
AuthorDisplayName = "Kỹ Sư Phần Mềm",
ContentSections = new List<BlogPostSection>
{
new BlogPostSection { HeaderText = "Giới Thiệu", BodyContent = "Đây là phần giới thiệu về chủ đề bài viết." },
new BlogPostSection { HeaderText = "Nội Dung Chính", BodyContent = "Chi tiết về các điểm cốt lõi và ví dụ minh họa." },
new BlogPostSection { HeaderText = "Kết Luận", BodyContent = "Tóm tắt các ý chính và gợi mở thêm." }
}
};
return View(blogPost);
}
}
}
3. View chính (`ViewPost.cshtml`)
@model App.Web.Models.BlogPost
@{
ViewData["Title"] = "Xem Bài Viết Blog";
}
<div class="blog-post">
<h1>Tiêu đề Bài Viết</h1>
@await Html.PartialAsync("_AuthorInfoDisplay", Model.AuthorDisplayName)
<div class="post-sections">
@foreach (var section in @Model.ContentSections)
{
@await Html.PartialAsync("_SectionContent", section)
}
</div>
</div>
4. Partial View cho thông tin tác giả (`_AuthorInfoDisplay.cshtml`)
@model string
<p class="author-name">Tác giả: <strong>@Model</strong></p>
5. Partial View cho từng phần nội dung (`_SectionContent.cshtml`)
@model App.Web.Models.BlogPostSection
<div class="post-section">
<h3 class="section-header">@Model.HeaderText</h3>
<p class="section-body">@Model.BodyContent</p>
</div>