在 MVC 架構中,View 負責將結果呈現給客戶端,通常會產生出 HTML 到瀏覽器端顯示。在 ASP.NET Core MVC 中,使用 Razor 語法來產生 View 結果,副檔名是 .cshtml,顧名思義就是用 C# 編譯產生的 HTML 檔案 XD。視圖檔案會放在 ~/Views
資料夾中,通常會以 Controller 作為資料夾,並用 Action 作為檔案名稱:
例如下列這個 MVC 範本產生出的 Controller/Action:
public class HomeController : Controller
{
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
}
就會使用 ~/Views/Home/About.cshtml
來產生 HTML 結構。
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>
<p>Use this area to provide additional information.</p>
@
符號開頭的是 Razor 語法,可以在大括號 {...}
中撰寫 C# 語法邏輯,或者直接接著變數來把值輸出到 HTML 中。今天不會說明 Razor 語法,想了解的讀者可以參考官方文件。
在 Action 中,可以直接使用 return View()
來讓 MVC 框架選擇對應的視圖,也可以用 return View("path/to/your/view.cshtml")
來指定要使用的視圖檔案。
要由 Controller 傳資料給 View 有幾種方式:
view model
直接用 @model
來指定這個 View 所使用的類別,在 View 中就可以用 @Model
來取得物件實例的資料。要把強型別的資料傳給 View 的方式,是在 return View()
的時候把物件實例作為參數傳入:
public class TodoController : Controller
{
public IActionResult Index()
{
var todos = _context.Todo.ToList();
return View(todos);
}
}
在 Razor 中就可以這樣來使用強型別的資料:
@model IEnumerable<ironman2018.Models.EntityFramework.TodoModel>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<table class="table">
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedAt)
</td>
<td>
@Html.DisplayFor(modelItem => item.Completed)
</td>
</tr>
}
</tbody>
</table>
弱型別資料分為 ViewData
和 ViewBag
兩種方式,型別都是在執行期間才會決定,比較不容易除錯,一般都還是以使用強型別為主。
ViewData
是 ViewDataDictionary 類別,類似 Dictionary<string, object>
這樣的資料結構。前面範例中看到的 ViewData["Message"] = "Your application description page.";
就是在 ViewData
中存入一個索引鍵為 Message
的字串,在 View 中以 ViewData["Message"]
來使用。
而 ViewBag
是 DynamicViewData 類別,這個方式都是使用 dynamic
來傳遞資料,使用方式跟 ViewData
相同,但可以用 .
來取資料。
通常同一個網站中大部分頁面的配置都類似,為了將不同頁面共用的區塊拉出來方便維護,會使用 Layout
來處理。例如下圖,網頁上方是 Header,下方是 Footer,左側有一塊 Navigation,不同頁面會有差別的只有 Content 的區塊:
圖片來源:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout
一般會將 Layout 的配置檔放在 ~/Views/Shared
目錄中,例如在我們專案中的 ~/Views/Shared/_Layout.cshtml
檔案(節錄部分),不同頁面顯示的內容會由 @RenderBody()
來顯示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - ironman2018</title>
</head>
<body>
<!-- 這邊是 Header -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
......
</div>
<div class="navbar-collapse collapse">
......
</div>
</div>
</nav>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© 2018 - ironman2018</p>
</footer>
</div>
</body>
在個別的 View 中,可以在最上方使用 Layout
屬性來指定要使用的配置,內容就會在配置中的 @RenderBody()
中渲染出來。
@{
Layout = "_Layout";
}
或者在 ~/Views/_ViewStart.cshtml
中設定專案中共用的配置(語法跟上面一樣)。
有些 HTML 很多的區塊,或者常常重複使用的區塊,可以用部分檢視的方式獨立出來。例如上面的 _Layout
中,如果覺得 Header 內容太多不好維護,可以建立一個 ~/Views/Shared/_HeaderPartial.cshtml
,並把 _Layout
修改成:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - ironman2018</title>
</head>
<body>
<!-- 引用 Header 的部分檢視 -->
<partial name="_HeaderPartial"/>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© 2018 - ironman2018</p>
</footer>
</div>
</body>
或者在 ~/Views/Todo/Index.cshtml
中,可以把顯示每筆資料的 Razor 獨立成一個強型別的部分檢視 ~/Views/Todo/_ItemPartial.cshtml
:
@model ironman2018.Models.EntityFramework.TodoModel
<tr>
<td>
@Model.Id
</td>
<td>
@Model.Title
</td>
<td>
@Model.CreatedAt
</td>
<td>
@Model.Completed
</td>
</tr>
同時修改原本的 View:
@model IEnumerable<ironman2018.Models.EntityFramework.TodoModel>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<table class="table">
<tbody>
@foreach (var item in Model)
{
<partial name="_ItemPartial" model="item"/>
}
</tbody>
</table>
如果是有寫過之前 ASP.NET MVC 的讀者可能會疑惑,部分檢視的語法應該是 @Html.Partial("_PartialName")
才對吧?!
這是因為 ASP.NET Core 提供了新的 TagHelper
語法,可以用類似 HTML 的方式來產生結果,這部分就留待明天揭曉吧~