Nguon: Microsoft Learn · .NET 8.0

Phần 3: Thêm View vào ứng dụng ASP.NET Core MVC

Nguồn: Part 3, add a view to an ASP.NET Core MVC app

Trong phần này, bạn sửa đổi class HelloWorldController để sử dụng các tệp Razor view. Điều này đóng gói gọn gàng quá trình tạo response HTML cho client (máy khách).

View template (mẫu giao diện) được tạo bằng Razor. Razor-based view template:

Hiện tại phương thức Index trả về một chuỗi có message trong class controller. Trong class HelloWorldController, thay thế phương thức Index bằng code sau:

csharp
public IActionResult Index()
{
    return View();
}

Code trước:

Controller method (phương thức controller):

Thêm view

Visual Studio

Nhấp chuột phải vào thư mục Views, rồi Add > New Folder và đặt tên thư mục là HelloWorld.

Nhấp chuột phải vào thư mục Views/HelloWorld, rồi Add > New Item.

Trong hộp thoại Add New Item, chọn Show All Templates.

Trong hộp thoại Add New Item - MvcMovie:

Visual Studio Code

Thêm view Index cho HelloWorldController:


Thay thế nội dung của tệp Razor view Views/HelloWorld/Index.cshtml bằng nội dung sau:

cshtml
@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>Hello from our View Template!</p>

Điều hướng đến https://localhost:{PORT}/HelloWorld:

Thay đổi views và layout pages

Chọn các menu link MvcMovie, HomePrivacy. Mỗi trang hiển thị cùng một layout menu. Layout menu được triển khai trong tệp Views/Shared/_Layout.cshtml.

Mở tệp Views/Shared/_Layout.cshtml.

Layout template cho phép:

Tìm dòng @RenderBody(). RenderBody là một placeholder (trình giữ chỗ) nơi tất cả các trang view cụ thể mà bạn tạo hiển thị, được bọc trong layout page. Ví dụ, nếu bạn chọn link Privacy, view Views/Home/Privacy.cshtml được render bên trong phương thức RenderBody.

Thay thế nội dung của tệp Views/Shared/_Layout.cshtml bằng markup sau. Các thay đổi được tô sáng:

cshtml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Movie App</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Movies" asp-action="Index">Movie App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2023 - Movie App - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Markup trên đã thực hiện các thay đổi sau:

Trong markup trên, thuộc tính asp-area="" anchor Tag Helper và giá trị thuộc tính đã bị bỏ qua vì ứng dụng này không sử dụng Areas.

Controller Movies chưa được triển khai. Tại thời điểm này, link Movie App không hoạt động.

Lưu các thay đổi và chọn link Privacy. Chú ý cách tiêu đề trên tab trình duyệt hiển thị Privacy Policy - Movie App thay vì Privacy Policy - MvcMovie.

Chọn link Home.

Chú ý rằng tiêu đề và anchor text hiển thị Movie App. Các thay đổi được thực hiện một lần trong layout template và tất cả các trang trên trang web phản ánh văn bản link mới và tiêu đề mới.

Kiểm tra tệp Views/_ViewStart.cshtml:

cshtml
@{
    Layout = "_Layout";
}

Tệp Views/_ViewStart.cshtml đưa tệp Views/Shared/_Layout.cshtml vào mỗi view. Thuộc tính Layout có thể được sử dụng để đặt một layout view khác, hoặc đặt thành null để không sử dụng tệp layout nào.

Mở tệp view Views/HelloWorld/Index.cshtml.

Thay đổi title và phần tử <h2> như tô sáng trong nội dung sau:

cshtml
@{
    ViewData["Title"] = "Movie List";
}

<h2>My Movie List</h2>

<p>Hello from our View Template!</p>

ViewData["Title"] = "Movie List"; trong code trên đặt thuộc tính Title của dictionary ViewData thành "Movie List". Thuộc tính Title được sử dụng trong phần tử HTML <title> trong layout page:

cshtml
<title>@ViewData["Title"] - Movie App</title>

Lưu thay đổi và điều hướng đến https://localhost:{PORT}/HelloWorld.

Chú ý rằng những điều sau đã thay đổi:

Nếu không có thay đổi trong trình duyệt, nó có thể là nội dung được cached (lưu trong bộ nhớ đệm) đang được xem. Nhấn Ctrl+F5 trong trình duyệt để buộc response từ server được tải. Tiêu đề trình duyệt được tạo với ViewData["Title"] mà chúng ta đặt trong view template Index.cshtml và phần bổ sung "- Movie App" được thêm vào trong tệp layout.

Nội dung trong view template Index.cshtml được hợp nhất với view template Views/Shared/_Layout.cshtml. Một HTML response duy nhất được gửi đến trình duyệt. Layout template giúp dễ dàng thực hiện các thay đổi áp dụng trên tất cả các trang trong ứng dụng. Để tìm hiểu thêm, xem Layout.

Tuy nhiên, một lượng nhỏ "dữ liệu", message "Hello from our View Template!" được hard-code (mã hóa cứng). Ứng dụng MVC có "V" (view), "C" (controller), nhưng chưa có "M" (model).

Truyền dữ liệu từ Controller sang View

Các controller action (hành động controller) được gọi để phản hồi yêu cầu URL đến. Class controller là nơi code được viết để xử lý các yêu cầu trình duyệt đến. Controller truy xuất dữ liệu từ data source (nguồn dữ liệu) và quyết định loại response nào sẽ gửi lại cho trình duyệt. View template có thể được sử dụng từ controller để tạo và định dạng HTML response cho trình duyệt.

Controller chịu trách nhiệm cung cấp dữ liệu cần thiết để view template render một response.

View template không nên:

View template chỉ nên làm việc với dữ liệu được controller cung cấp cho nó. Duy trì "separation of concerns" này giúp giữ code:

Hiện tại, phương thức Welcome trong class HelloWorldController lấy tham số nameID rồi xuất các giá trị trực tiếp đến trình duyệt.

Thay vì controller render response này như một chuỗi, hãy thay đổi controller để sử dụng view template. View template tạo ra dynamic response (phản hồi động), có nghĩa là dữ liệu phù hợp phải được truyền từ controller sang view để tạo response. Thực hiện điều này bằng cách có controller đặt dữ liệu động (tham số) mà view template cần trong dictionary ViewData. View template sau đó có thể truy cập dữ liệu động.

Trong HelloWorldController.cs, thay đổi phương thức Welcome để thêm giá trị MessageNumTimes vào dictionary ViewData.

Dictionary ViewData là một đối tượng động, có nghĩa là bất kỳ kiểu nào cũng có thể được sử dụng. Đối tượng ViewData không có thuộc tính được định nghĩa cho đến khi có gì đó được thêm vào. MVC model binding system tự động ánh xạ các tham số được đặt tên namenumTimes từ query string sang các tham số trong phương thức. HelloWorldController đầy đủ:

csharp
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers;

public class HelloWorldController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Welcome(string name, int numTimes = 1)
    {
        ViewData["Message"] = "Hello " + name;
        ViewData["NumTimes"] = numTimes;
        return View();
    }
}

Đối tượng dictionary ViewData chứa dữ liệu sẽ được truyền vào view.

Tạo Welcome view template có tên Views/HelloWorld/Welcome.cshtml.

Bạn sẽ tạo một vòng lặp trong view template Welcome.cshtml hiển thị "Hello" NumTimes lần. Thay thế nội dung của Views/HelloWorld/Welcome.cshtml bằng nội dung sau:

cshtml
@{
    ViewData["Title"] = "Welcome";
}

<h2>Welcome</h2>

<ul>
    @for (int i = 0; i < (int)ViewData["NumTimes"]!; i++)
    {
        <li>@ViewData["Message"]</li>
    }
</ul>

Lưu các thay đổi và duyệt đến URL sau:

https://localhost:{PORT}/HelloWorld/Welcome?name=Rick&numtimes=4

Dữ liệu được lấy từ URL và truyền vào controller bằng MVC model binder. Controller đóng gói dữ liệu vào dictionary ViewData và truyền đối tượng đó vào view. View sau đó render dữ liệu dưới dạng HTML cho trình duyệt.

Trong mẫu trước, dictionary ViewData được sử dụng để truyền dữ liệu từ controller sang view. Ở phần sau trong hướng dẫn, view model được sử dụng để truyền dữ liệu từ controller sang view. Phương pháp view model để truyền dữ liệu được ưa thích hơn phương pháp dictionary ViewData.

Trong hướng dẫn tiếp theo, một database phim được tạo.

Trước: Thêm Controller | Tiếp theo: Thêm Model