---
title: Views
description: Giới thiệu chi tiết về Views trong ASP.NET Core MVC — cách tạo view, truyền dữ liệu, ViewData, ViewBag, và layout.
---

## Views là gì?

Trong mô hình **Model-View-Controller (MVC)**, View xử lý **trình bày dữ liệu** và **tương tác người dùng** của ứng dụng. View là một HTML template với **Razor markup** nhúng vào. Razor markup là code tương tác với HTML markup để tạo webpage đượ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.

---

## Cấu trúc thư mục Views

Thông thường, các view files được nhóm vào các folders có tên theo từng controller của ứng dụng. Các folders này được lưu trong thư mục **Views** ở root của ứng dụng:

```text
MyApp/
├── Views/
│   ├── Home/            ← Views cho HomeController
│   │   ├── Index.cshtml
│   │   ├── About.cshtml
│   │   └── Contact.cshtml
│   ├── Products/        ← Views cho ProductsController
│   │   ├── Index.cshtml
│   │   ├── Details.cshtml
│   │   └── Create.cshtml
│   └── Shared/          ← Views dùng chung
│       ├── _Layout.cshtml
│       └── _PartialView.cshtml
```

Controller `Home` được đại diện bởi thư mục `Home` trong `Views`. Thư mục này chứa views cho About, Contact và Index. Khi người dùng request một trong 3 webpages này, các controller actions trong Home controller quyết định view nào được dùng để build và trả về webpage.

---

## Lợi ích của Views

Views giúpEstablish **separation of concerns** trong ứ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 SoC design giúp ứng dụng **modular**, mang lại nhiều lợi ích:

| Lợi ích            | Mô tả                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| **Dễ bảo trì**     | Ứng dụng được tổ chức tốt hơn. Views được nhóm theo tính năng, dễ tìm khi làm việc trên một tính năng |
| **Loose coupling** | Có thể build và update views riêng biệt với business logic và data access components                  |
| **Dễ test**        | Các phần giao diện người dùng dễ test vì views là các đơn vị riêng biệt                               |
| **Giảm trùng lặp** | Ít khả năng vô tình lặp lại các phần giao diện                                                        |

---

## Layout

Sử dụng **Layouts** để cung cấp các phần webpage nhất quán và giảm code trùng lặp. Layouts thường chứa header, navigation, menu elements và footer. Header và footer thường chứa boilerplate markup cho nhiều metadata elements và links đến script và style assets.

```cshtml
<!DOCTYPE html>
<html>
<head>
    <title>@ViewData["Title"] - My App</title>
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav>@await Html.PartialAsync("_Menu")</nav>
    </header>

    <div class="container">
        @RenderBody()  @* Nội dung page được render ở đây *@
    </div>

    <footer>
        &copy; 2025 My App
    </footer>

    <script src="~/js/site.js"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
```

Mỗi view sử dụng layout bằng directive `@model` ở đầu file:

```cshtml
@model MyViewModel
@{
    Layout = "_Layout";
}

<h2>@Model.Title</h2>
<p>@Model.Description</p>
```

---

## Partial Views

**Partial views** giảm code trùng lặp bằng cách quản lý các phần có thể tái sử dụng của views. Partial view hữu ích cho phần author biography trên blog xuất hiện ở nhiều views. Author biography là ordinary view content và không cần code thực thi để produce content cho webpage.

```cshtml
@* Partial view: AuthorBio.cshtml *@
<div class="author-bio">
    <img src="@Model.AvatarUrl" alt="@Model.AuthorName" />
    <h4>@Model.AuthorName</h4>
    <p>@Model.Bio</p>
</div>
```

Render partial view từ một view khác:

```cshtml
@* Trong một view khác *@
<partial name="AuthorBio" model="@Model.Author" />
```

---

## View Components

**View Components** tương tự partial views ở chỗ cho phép giảm code trùng lặp, nhưng phù hợp cho view content **cần code chạy trên server** để render webpage. View Components hữu ích khi rendered content cần database interaction, ví dụ shopping cart trên website.

```csharp
public class ShoppingCartViewComponent : ViewComponent
{
    private readonly ICartService _cartService;

    public ShoppingCartViewComponent(ICartService cartService)
    {
        _cartService = cartService;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var items = await _cartService.GetCartItemsAsync();
        return View(items);
    }
}
```

```cshtml
@* Views/Shared/Components/ShoppingCart/Default.cshtml *@
@model IEnumerable<CartItem>

<div class="shopping-cart">
    <h3>Giỏ hàng của bạn</h3>
    @if (Model.Any())
    {
        <ul>
            @foreach (var item in Model)
            {
                <li>@item.ProductName - @item.Quantity x @item.Price</li>
            }
        </ul>
    }
    else
    {
        <p>Giỏ hàng trống.</p>
    }
</div>
```

Sử dụng trong layout hoặc view:

```cshtml
@await Component.InvokeAsync("ShoppingCart")
```

---

## Tạo View

Views cụ thể cho một controller được tạo trong thư mục `Views/[ControllerName]`. Views dùng chung giữa các controllers được đặt trong `Views/Shared`.

Để tạo view, thêm file mới và đặt tên giống tên action tương ứng với extension `.cshtml`. Để tạo view tương ứng với action `About` trong `HomeController`, tạo file `About.cshtml` trong `Views/Home`:

```cshtml
@{
    ViewData["Title"] = "About";
}

<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

<p>Sử dụng khu vực này để cung cấp thông tin bổ sung.</p>
```

### Razor Markup cơ bản

Razor markup bắt đầu với symbol `@`. Chạy C# statements bằng cách đặt code C# trong Razor code blocks được set off bằng curly braces:

```cshtml
@{
    var greeting = "Xin chào";
    var userName = "Người dùng";
}

<p>@greeting, @userName!</p>
```

Hiển thị giá trị trong HTML bằng cách reference với `@`:

```cshtml
<h2>@ViewData["Title"]</h2>
<p>@Model.Description</p>
```

---

## Controller trả về View

Views thường được trả về từ actions như **ViewResult**, là một loại **ActionResult**. Controller kế thừa từ `Controller` có thể dùng helper method `View()`:

```csharp
public IActionResult About()
{
    ViewData["Message"] = "Trang mô tả ứng dụng của bạn.";
    return View();
}
```

### Các overload của View()

```csharp
// Trả về view cùng tên với action
return View();

// Trả về view có tên cụ thể
return View("Orders");

// Trả về view với model
return View(order);

// Trả về view cụ thể với model
return View("OrderDetails", order);
```

---

## View Discovery

Khi một action trả về view, một quá trình gọi là **view discovery** xác định view file nào được sử dụng dựa trên view name.

Mặc định, method `View()` trả về view cùng tên với action method. Runtime tìm view theo thứ tự:

```mermaid
flowchart LR
    A["return View();"] --> B1["1️⃣ Views/[Controller]/[Action].cshtml"]
    B1 -->|Không tìm thấy| B2["2️⃣ Views/Shared/[Action].cshtml"]
    B2 -->|Không tìm thấy| C["❌ Lỗi: View not found"]
```

### Các cách chỉ định view path

```csharp
// Absolute path từ app root — phải có extension .cshtml
return View("Views/Home/About.cshtml");

// Relative path
return View("../Manage/Index");

// Relative path với prefix ./ để chỉ controller hiện tại
return View("./About");
```

---

## Truyền dữ liệu đến Views

Có nhiều cách để truyền dữ liệu đến views:

```mermaid
flowchart TD
    A["Truyền dữ liệu đến Views"] --> B["Strongly typed: @model"]
    A --> C["Weakly typed: ViewData / ViewBag"]
```

---

### Strongly Typed Data (ViewModel)

Cách mạnh mẽ nhất là chỉ định **model type** trong view — gọi là **viewmodel**. Sử dụng viewmodel cho phép view tận dụng **strong type checking** và hỗ trợ **IntelliSense**.

#### Khai báo model trong view

```cshtml
@model WebApplication1.ViewModels.Address

<h2>Liên hệ</h2>
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">ĐT:</abbr> 425.555.0100
</address>
```

#### Controller truyền model

```csharp
public IActionResult Contact()
{
    var viewModel = new Address
    {
        Name = "Công ty ABC",
        Street = "123 Đường ABC",
        City = "TP. Hồ Chí Minh",
        State = "HCM",
        PostalCode = "70000"
    };

    return View(viewModel);
}
```

#### ViewModel class

```csharp
namespace WebApplication1.ViewModels
{
    public class Address
    {
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
    }
}
```

> **Lưu ý:** Nên sử dụng **POCO (Plain Old CLR Object) viewmodels** — có ít hoặc không có behavior (methods). ViewModels được lưu trong folder `Models` hoặc folder `ViewModels` riêng ở root của ứng dụng.

---

### ViewData

**ViewData** là **ViewDataDictionary** object, truy cập qua string keys. String data có thể được stored và used trực tiếp, nhưng phải **cast** các object values sang specific types khi extract.

#### Trong Controller

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

    return View();
}
```

#### Trong View

```cshtml
@{
    // Vì Address không phải string, cần cast
    var address = ViewData["Address"] as Address;
}

@ViewData["Greeting"] World!

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

---

### [ViewData] Attribute

Properties trên controllers được đánh dấu với attribute `[ViewData]` sẽ có values được stored và loaded từ dictionary:

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

    public IActionResult About()
    {
        Title = "Về chúng tôi";
        ViewData["Message"] = "Trang mô tả ứng dụng.";
        return View();
    }
}
```

```cshtml
@* Trong layout *@
<head>
    <title>@ViewData["Title"] - My App</title>
</head>
```

---

### ViewBag

**ViewBag** là wrapper quanh ViewData, cung cấp **dynamic properties** cho underlying ViewData collection. ViewBag tiện lợi hơn vì **không cần cast**.

#### Controller

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

    return View();
}
```

#### View

```cshtml
@ViewBag.Greeting World!

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

---

### So sánh ViewData vs ViewBag vs ViewModel

| Tiêu chí          | ViewData           | ViewBag           | ViewModel (@model) |
| ----------------- | ------------------ | ----------------- | ------------------ |
| **Type checking** | Runtime (dynamic)  | Runtime (dynamic) | Compile-time ✅    |
| **IntelliSense**  | ❌                 | ❌                | ✅                 |
| **Cast required** | ✅ Có              | ❌ Không          | ❌ Không           |
| **Syntax**        | `@ViewData["Key"]` | `@ViewBag.Key`    | `@Model.Property`  |
| **Khuyến nghị**   | Hạn chế            | Hạn chế           | **Ưu tiên dùng**   |

> **Khuyến nghị:** Nên sử dụng **ViewModel (@model)** làm cách chính để truyền dữ liệu. ViewData và ViewBag dễ gây lỗi runtime vì không có compile-time checking.

---

## Tài liệu tham khảo

- [Views in ASP.NET Core MVC](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/overview)
- [Layout in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout)
- [Partial Views](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial)
- [View Components](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components)
- [Razor Syntax](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor)
