Nguon: Microsoft Learn · .NET 8.0

Views (Hiển thị) trong ASP.NET Core MVC

Nguồn: Views in ASP.NET Core MVC

Bởi Steve SmithDave Brock

Tài liệu này giải thích views được sử dụng trong các ứng dụng ASP.NET Core MVC. Để biết thông tin về Razor Pages, xem Kiến trúc và khái niệm Razor Pages trong ASP.NET Core.

Trong mẫu Model-View-Controller (MVC), view (hiển thị) xử lý việc trình bày dữ liệu và tương tác người dùng của ứng dụng. Một view là một HTML template (mẫu HTML) với Razor markup nhúng vào. Razor markup là code tương tác với HTML markup để tạo ra một trang web được gửi đến client.

Trong ASP.NET Core MVC, views là các file .cshtml sử dụng ngôn ngữ lập trình C# trong Razor markup. Thông thường, các file view được nhóm vào các thư mục được đặt tên theo từng controller của ứng dụng. Các thư mục được lưu trữ trong thư mục Views ở gốc của ứng dụng.

Thư mục Home bên trong thư mục Views đại diện cho controller Home. Thư mục Home chứa các view cho các trang web About, Contact, và Index (trang chủ). Khi người dùng yêu cầu một trong ba trang web này, các controller actions trong controller Home xác định view nào trong ba view được sử dụng để xây dựng và trả về trang web cho người dùng.

Sử dụng layouts để cung cấp các phần trang web nhất quán và giảm sự lặp lại code. Layouts thường chứa header (tiêu đề), các phần tử điều hướng và menu, và footer (chân trang). Header và footer thường chứa markup soạn sẵn cho nhiều phần tử metadata và liên kết đến script và style assets. Layouts giúp bạn tránh markup soạn sẵn này trong các view của mình.

Partial views giảm sự trùng lặp code bằng cách quản lý các phần có thể tái sử dụng của views. Ví dụ, một partial view hữu ích cho tiểu sử tác giả trên một trang blog xuất hiện trong nhiều view.

View components tương tự như partial views ở chỗ chúng cho phép bạn giảm code lặp lại, nhưng chúng phù hợp với nội dung view yêu cầu code chạy trên server để render (hiển thị) trang web. View components hữu ích khi nội dung được render yêu cầu tương tác cơ sở dữ liệu, chẳng hạn như giỏ hàng của một trang web.

Lợi ích của việc sử dụng views

Views giúp thiết lập separation of concerns (phân tách mối quan tâm) trong một ứng dụng MVC bằng cách tách markup giao diện người dùng khỏi các phần khác của ứng dụng. Việc tuân theo thiết kế SoC làm cho ứng dụng của bạn theo dạng module (module hóa), mang lại một số lợi ích:

Tạo một view

Các view dành riêng cho một controller được tạo trong thư mục Views/[ControllerName]. Các view được chia sẻ giữa các controller được đặt trong thư mục Views/Shared. Để tạo một view, thêm một file mới và đặt cho nó cùng tên với action controller liên quan với phần mở rộng file .cshtml. Để tạo một view tương ứng với action About trong controller Home, tạo file About.cshtml trong thư mục Views/Home:

cshtml
@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>Use this area to provide additional information.</p>

Razor markup bắt đầu bằng ký hiệu @. Chạy các câu lệnh C# bằng cách đặt code C# trong Razor code blocks (khối code Razor) được bao quanh bởi dấu ngoặc nhọn ({ ... }). Ví dụ, xem phép gán "About" cho ViewData["Title"] được hiển thị trước đó.

Controllers chỉ định views như thế nào

Views thường được trả về từ các actions như một ViewResult, là một loại ActionResult. Action method của bạn có thể tạo và trả về một ViewResult trực tiếp, nhưng điều đó không phổ biến. Vì hầu hết các controller kế thừa từ Controller, bạn đơn giản sử dụng method helper View để trả về ViewResult:

HomeController.cs:

csharp
public IActionResult About()
{
    ViewData["Message"] = "Your application description page.";

    return View();
}

Method helper View có một số overloads. Bạn có thể tùy chọn chỉ định:

``csharp return View("Orders"); ``

``csharp return View(Orders); ``

``csharp return View("Orders", Orders); ``

View discovery (Khám phá view)

Khi một action trả về một view, một quá trình gọi là view discovery (khám phá view) diễn ra. Quá trình này xác định file view nào được sử dụng dựa trên tên view.

Hành vi mặc định của method View (return View();) là trả về một view có cùng tên với action method mà nó được gọi từ đó. Ví dụ, tên About của ActionResult trong controller được sử dụng để tìm kiếm một file view tên là About.cshtml. Đầu tiên, runtime tìm trong thư mục Views/[ControllerName] cho view. Nếu không tìm thấy view phù hợp ở đó, nó tìm kiếm trong thư mục Shared cho view.

View discovery tìm kiếm một file view phù hợp theo thứ tự này:

  1. Views/\[ControllerName]/\[ViewName].cshtml
  2. Views/Shared/\[ViewName].cshtml

Đường dẫn file view có thể được cung cấp thay vì tên view. Nếu sử dụng đường dẫn tuyệt đối bắt đầu từ gốc ứng dụng (tùy chọn bắt đầu bằng "/" hoặc "~/"), phần mở rộng .cshtml phải được chỉ định:

csharp
return View("Views/Home/About.cshtml");

Bạn cũng có thể sử dụng đường dẫn tương đối để chỉ định các view trong các thư mục khác nhau mà không cần phần mở rộng .cshtml. Trong HomeController, bạn có thể trả về view Index của các view Manage của bạn với một đường dẫn tương đối:

csharp
return View("../Manage/Index");

Tương tự, bạn có thể chỉ ra thư mục dành riêng cho controller hiện tại với tiền tố "./":

csharp
return View("./About");

Truyền dữ liệu vào views

Truyền dữ liệu vào views bằng một số cách tiếp cận:

Dữ liệu Strongly-typed (viewmodel)

Cách tiếp cận mạnh mẽ nhất là chỉ định loại model trong view. Model này thường được gọi là viewmodel. Bạn truyền một instance (thể hiện) của kiểu viewmodel cho view từ action.

Sử dụng viewmodel để truyền dữ liệu vào view cho phép view tận dụng kiểm tra kiểu mạnh. Kiểu mạnh (hoặc strongly typed) có nghĩa là mọi biến và hằng số đều có kiểu được định nghĩa rõ ràng (ví dụ: string, int, hoặc DateTime). Tính hợp lệ của các kiểu được sử dụng trong view được kiểm tra tại thời điểm compile (biên dịch).

Visual Studio và Visual Studio Code liệt kê các thành viên class strongly typed sử dụng tính năng gọi là IntelliSense. Khi bạn muốn xem các thuộc tính của một viewmodel, gõ tên biến cho viewmodel theo sau là dấu chấm (.). Điều này giúp bạn viết code nhanh hơn với ít lỗi hơn.

Chỉ định một model bằng cách sử dụng chỉ thị @model. Sử dụng model với @Model:

cshtml
@model WebApplication1.ViewModels.Address

<h2>Contact</h2>
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

Để cung cấp model cho view, controller truyền nó như một tham số:

csharp
public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    var viewModel = new Address()
    {
        Name = "Microsoft",
        Street = "One Microsoft Way",
        City = "Redmond",
        State = "WA",
        PostalCode = "98052-6399"
    };

    return View(viewModel);
}

Không có hạn chế về loại model bạn có thể cung cấp cho view. Chúng tôi khuyến nghị sử dụng viewmodel POCO (Plain Old CLR Object - Đối tượng CLR đơn giản) với ít hoặc không có hành vi (method) được định nghĩa. Thông thường, các class viewmodel được lưu trữ trong thư mục Models hoặc thư mục ViewModels riêng biệt ở gốc của ứng dụng.

Dữ liệu Weakly-typed (ViewData, thuộc tính [ViewData], và ViewBag)

ViewBag không có sẵn theo mặc định để sử dụng trong các class Razor Pages PageModel.

Ngoài các view kiểu mạnh, views còn có quyền truy cập vào một bộ sưu tập kiểu yếu (còn gọi là loosely typed) của dữ liệu. Không giống như kiểu mạnh, kiểu yếu (hoặc loose types) có nghĩa là bạn không khai báo rõ ràng kiểu dữ liệu bạn đang sử dụng. Bạn có thể sử dụng bộ sưu tập dữ liệu kiểu yếu để truyền lượng nhỏ dữ liệu vào và ra khỏi controllers và views.

Truyền dữ liệu giữa ...Ví dụ
Controller và một viewĐiền vào dropdown list với dữ liệu.
View và một layout viewThiết lập nội dung phần tử <title> trong layout view từ một file view.
Partial view và một viewMột widget hiển thị dữ liệu dựa trên trang web mà người dùng yêu cầu.

Bộ sưu tập này có thể được tham chiếu thông qua thuộc tính ViewData hoặc ViewBag trên controllers và views. Thuộc tính ViewData là một dictionary (từ điển) của các đối tượng kiểu yếu. Thuộc tính ViewBag là một wrapper (bao bọc) xung quanh ViewData cung cấp các thuộc tính động cho bộ sưu tập ViewData cơ bản. Lưu ý: Tra cứu key không phân biệt chữ hoa/thường cho cả ViewDataViewBag.

ViewDataViewBag được giải quyết động tại runtime. Vì chúng không cung cấp kiểm tra kiểu tại thời điểm compile, cả hai thường dễ bị lỗi hơn khi sử dụng viewmodel.

ViewData

ViewData là một đối tượng ViewDataDictionary được truy cập thông qua các key string. Dữ liệu chuỗi có thể được lưu trữ và sử dụng trực tiếp mà không cần ép kiểu, nhưng bạn phải ép kiểu các giá trị đối tượng ViewData khác sang các kiểu cụ thể khi bạn trích xuất chúng.

csharp
public IActionResult SomeAction()
{
    ViewData["Greeting"] = "Hello";
    ViewData["Address"]  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}

Làm việc với dữ liệu trong view:

cshtml
@{
    // Since Address isn't a string, it requires a cast.
    var address = ViewData["Address"] as Address;
}

@ViewData["Greeting"] World!

<address>
    @address.Name<br>
    @address.Street<br>
    @address.City, @address.State @address.PostalCode
</address>

Thuộc tính [ViewData]

Một cách tiếp cận khác sử dụng ViewDataDictionaryViewDataAttribute. Các thuộc tính trên controllers hoặc Razor Page models được đánh dấu bằng thuộc tính [ViewData] có giá trị của chúng được lưu trữ và tải từ dictionary.

csharp
public class HomeController : Controller
{
    [ViewData]
    public string Title { get; set; }

    public IActionResult About()
    {
        Title = "About Us";
        ViewData["Message"] = "Your application description page.";

        return View();
    }
}

Trong layout, tiêu đề được đọc từ ViewData dictionary:

cshtml
<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

ViewBag

ViewBag không có sẵn theo mặc định để sử dụng trong các class Razor Pages PageModel.

ViewBag là một đối tượng Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData cung cấp quyền truy cập động vào các đối tượng được lưu trữ trong ViewData. ViewBag có thể thuận tiện hơn để làm việc vì nó không yêu cầu ép kiểu.

csharp
public IActionResult SomeAction()
{
    ViewBag.Greeting = "Hello";
    ViewBag.Address  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}
cshtml
@ViewBag.Greeting World!

<address>
    @ViewBag.Address.Name<br>
    @ViewBag.Address.Street<br>
    @ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode
</address>

Sử dụng ViewDataViewBag đồng thời

ViewDataViewBag tham chiếu đến cùng một bộ sưu tập ViewData cơ bản, bạn có thể sử dụng cả ViewDataViewBag và kết hợp giữa chúng khi đọc và ghi giá trị.

Tóm tắt sự khác biệt giữa ViewDataViewBag

Khi nào sử dụng ViewData hoặc ViewBag

Cả ViewDataViewBag đều là các cách tiếp cận hợp lệ để truyền lượng nhỏ dữ liệu giữa controllers và views. Sự lựa chọn dựa trên sở thích. Cả hai cách đều được giải quyết động tại runtime và do đó dễ gây ra lỗi runtime. Một số nhóm phát triển tránh chúng.

Dynamic views (View động)

Các view không khai báo kiểu model bằng @model nhưng có một instance model được truyền cho chúng (ví dụ: return View(Address);) có thể tham chiếu các thuộc tính của instance một cách động:

cshtml
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

Tính năng này cung cấp sự linh hoạt nhưng không cung cấp bảo vệ biên dịch hoặc IntelliSense.

Các tính năng view khác

Tag Helpers giúp dễ dàng thêm hành vi phía server vào các thẻ HTML hiện có. Sử dụng Tag Helpers tránh phải viết code tùy chỉnh hoặc helpers trong các view của bạn.

Tạo HTML markup tùy chỉnh có thể đạt được với nhiều HTML Helpers tích hợp sẵn. Logic giao diện người dùng phức tạp hơn có thể được xử lý bởi View Components. View components cung cấp cùng SoC mà controllers và views cung cấp. Chúng có thể loại bỏ nhu cầu về các actions và views xử lý dữ liệu được sử dụng bởi các phần tử giao diện người dùng phổ biến.

Giống như nhiều khía cạnh khác của ASP.NET Core, views hỗ trợ dependency injection (tiêm phụ thuộc), cho phép các service (dịch vụ) được tiêm vào views.

CSS isolation (Cô lập CSS)

Cô lập các style CSS cho các trang, view và component riêng lẻ để giảm hoặc tránh:

Để thêm một scoped CSS file (file CSS có phạm vi) cho một trang hoặc view, đặt các style CSS trong một file .cshtml.css đi kèm khớp với tên của file .cshtml. Trong ví dụ sau, một file Index.cshtml.css cung cấp các style CSS chỉ được áp dụng cho trang hoặc view Index.cshtml.

Pages/Index.cshtml.css (Razor Pages) hoặc Views/Index.cshtml.css (MVC):

css
h1 {
    color: red;
}

CSS isolation xảy ra tại thời điểm build. Framework viết lại các CSS selectors để khớp với markup được render bởi các trang hoặc view của ứng dụng. Các style CSS được viết lại được bundle (gói) và tạo ra như một static asset, {APP ASSEMBLY}.styles.css. Placeholder {APP ASSEMBLY} là tên assembly của project.

Trong nội dung <head> của Pages/Shared/_Layout.cshtml (Razor Pages) hoặc Views/Shared/_Layout.cshtml (MVC), thêm hoặc xác nhận sự hiện diện của link đến các style CSS được bundle:

html
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />

Hỗ trợ CSS preprocessor

CSS preprocessors (bộ xử lý trước CSS) hữu ích để cải thiện phát triển CSS bằng cách sử dụng các tính năng như biến, lồng nhau, module, mixins, và kế thừa. Mặc dù CSS isolation không hỗ trợ CSS preprocessors như Sass hoặc Less, việc tích hợp CSS preprocessors liền mạch miễn là việc biên dịch preprocessor xảy ra trước khi framework viết lại CSS selectors trong quá trình build.

Cấu hình CSS isolation

CSS isolation cho phép cấu hình cho một số tình huống nâng cao, chẳng hạn như khi có sự phụ thuộc vào các công cụ hoặc quy trình làm việc hiện có.

Tùy chỉnh định dạng scope identifier

Theo mặc định, scope identifiers sử dụng định dạng b-{STRING}. Để tùy chỉnh định dạng scope identifier, cập nhật file project sang một mẫu mong muốn:

xml
<ItemGroup>
  <None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Tắt automatic bundling

Để từ chối cách framework publish và tải các scoped files tại runtime, sử dụng thuộc tính DisableScopedCssBundling:

xml
<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>