本節主要介紹在上一節中通過搭建基架而創建的Razor頁面,並做一些UI改變。 一、創建、刪除、詳細信息和編輯頁面 1、雙擊Pages/Movies/Index.cshtml.cs文件,這是一個Razor頁面模型: ① 第13行:表示該Razor頁面派生自PageModel。約定:PageModel派 ...
本節主要介紹在上一節中通過搭建基架而創建的Razor頁面,並做一些UI改變。
一、創建、刪除、詳細信息和編輯頁面
1、雙擊Pages/Movies/Index.cshtml.cs文件,這是一個Razor頁面模型:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 using Microsoft.AspNetCore.Mvc.RazorPages; 7 using Microsoft.EntityFrameworkCore; 8 using RazorPagesMovie.Data; 9 using RazorPagesMovie.Models; 10 11 namespace RazorPagesMovie 12 { 13 public class IndexModel : PageModel 14 { 15 private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context; 16 17 public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context) 18 { 19 _context = context; 20 } 21 22 public IList<Movie> Movie { get;set; } 23 24 public async Task OnGetAsync() 25 { 26 Movie = await _context.Movie.ToListAsync(); 27 } 28 } 29 }
① 第13行:表示該Razor頁面派生自PageModel。約定:PageModel派生的類稱為<PageName>Model。
② 第17行:表示這是一個構造函數,使用依賴關係註入將RazorPagesMovieContent添加到頁。所有已搭建基架的頁面都遵循這個模式。
③ 第24行:表示對頁面發出請求時,OnGetAsync方法向Razor頁面返回影片列表。調用OnGetAsync或OnGet以初始化頁面的狀態。OnGetAsync方法將獲得的影片列表顯示出來。當OnGet返回void或OnGetAsync返回task時,使用任何返回語句。因為此時返回的Movie對象,在程式中做了定義(第22行)
2、雙擊Pages/Movies/Create.cshtml.cs文件,這也是一個Razor頁面模型:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 using Microsoft.AspNetCore.Mvc.RazorPages; 7 using Microsoft.AspNetCore.Mvc.Rendering; 8 using RazorPagesMovie.Data; 9 using RazorPagesMovie.Models; 10 11 namespace RazorPagesMovie 12 { 13 public class CreateModel : PageModel 14 { 15 private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context; 16 17 public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context) 18 { 19 _context = context; 20 } 21 22 public IActionResult OnGet() 23 { 24 return Page(); 25 } 26 27 [BindProperty] 28 public Movie Movie { get; set; } 29 30 // To protect from overposting attacks, please enable the specific properties you want to bind to, for 31 // more details see https://aka.ms/RazorPagesCRUD. 32 public async Task<IActionResult> OnPostAsync() 33 { 34 if (!ModelState.IsValid) 35 { 36 return Page(); 37 } 38 39 _context.Movie.Add(Movie); 40 await _context.SaveChangesAsync(); 41 42 return RedirectToPage("./Index"); 43 } 44 } 45 }
④ 第32、22行:當返回類型是IActionResult或Task<IActionResult>時,必須提供返回語句。
3、雙擊Pages/Movies/Index.cshtml文件,這是一個Razor頁面:
1 @page 2 @model RazorPagesMovie.IndexModel 3 4 @{ 5 ViewData["Title"] = "Index"; 6 } 7 8 <h1>Index</h1> 9 10 <p> 11 <a asp-page="Create">Create New</a> 12 </p> 13 <table class="table"> 14 <thead> 15 <tr> 16 <th> 17 @Html.DisplayNameFor(model => model.Movie[0].Title) 18 </th> 19 <th> 20 @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate) 21 </th> 22 <th> 23 @Html.DisplayNameFor(model => model.Movie[0].Genre) 24 </th> 25 <th> 26 @Html.DisplayNameFor(model => model.Movie[0].Price) 27 </th> 28 <th></th> 29 </tr> 30 </thead> 31 <tbody> 32 @foreach (var item in Model.Movie) { 33 <tr> 34 <td> 35 @Html.DisplayFor(modelItem => item.Title) 36 </td> 37 <td> 38 @Html.DisplayFor(modelItem => item.ReleaseDate) 39 </td> 40 <td> 41 @Html.DisplayFor(modelItem => item.Genre) 42 </td> 43 <td> 44 @Html.DisplayFor(modelItem => item.Price) 45 </td> 46 <td> 47 <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> | 48 <a asp-page="./Details" asp-route-id="@item.ID">Details</a> | 49 <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a> 50 </td> 51 </tr> 52 } 53 </tbody> 54 </table>
Razor可以從HTML轉換為C#或Razor特定的標記。當@符號後面跟著Razor保留關鍵字時,它會轉換為Razor特定標記,否則會轉換為C#。
① 第1行:@page指令,它是一個Razor指令的一個示例。該指令表示將文件轉換為一個MVC操作。這意味著它可以處理請求。@page必須是頁面上第一個Razor指令。
② 第17-26行:@Html 這是一系列的使用Lambda表達式的HTML幫助程式。DisplayNameFor HTML幫助程式檢查Lambda表達式引用的Tile、ReleaseDate等屬性來確定顯示名稱。檢查Lambda表達式(而非求值),意味著model、model.Movie或model.Movie[0]為null或空時,不會存在任何訪問衝突。
③ 第35-44行:@Html.DisplayFor是對Lambda表達式進行求值,將獲得該模型的屬性值。
④ 第2行:@model指令,指定傳遞給Razor頁面的模型類型。這個例子中的模型類型,就是第1段中派生於PageModel類的IndexModel模型。
⑤ 第4-6行:@符號後面沒有Razor關鍵字,表示這是C#的一個示例。{}大括弧中是C#代碼塊。這個頁面的引用的模型是IndexModel,它派生於PageModel,PageModel基類中包含ViewData字典屬性,可用於將數據傳遞到某個視圖。我們可以採用鍵值對的模式將對象添加到ViewData字典中。這裡,“Title”屬性被添加到ViewData字典中。而“Title”屬性又被用於/Pages/Shared/_Layout.cshtml文件中。見第4節中的第③條註釋。
4、雙擊/Pages/Shared/_Layout.cshtml文件
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>@ViewData["Title"] - RazorPagesMovie</title> 7 <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> 8 <link rel="stylesheet" href="~/css/site.css" /> 9 </head> 10 <body> 11 <header> 12 <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> 13 <div class="container"> 14 <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a> 15 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent" 16 aria-expanded="false" aria-label="Toggle navigation"> 17 <span class="navbar-toggler-icon"></span> 18 </button> 19 <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse"> 20 <ul class="navbar-nav flex-grow-1"> 21 <li class="nav-item"> 22 <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a> 23 </li> 24 <li class="nav-item"> 25 <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a> 26 </li> 27 </ul> 28 </div> 29 </div> 30 </nav> 31 </header> 32 <div class="container"> 33 <main role="main" class="pb-3"> 34 @RenderBody() 35 </main> 36 </div> 37 38 <footer class="border-top footer text-muted"> 39 <div class="container"> 40 © 2019 - RazorPagesMovie - <a asp-area="" asp-page="/Privacy">Privacy</a> 41 </div> 42 </footer> 43 44 <script src="~/lib/jquery/dist/jquery.min.js"></script> 45 <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> 46 <script src="~/js/site.js" asp-append-version="true"></script> 47 48 @RenderSection("Scripts", required: false) 49 </body> 50 </html>
① 這是一個頁面佈局的模板(類似於母版頁)。它允許HTML容器具有如下佈局:在一個位置指定;應用於站點中的多個頁面。
② 第34行:@RenderBody(),是顯示全部頁面專用視圖的占位符。
③ 第6行:通過@ViewData["Title"]將字典中的對象“Title”的值取出來,和字元串‘- RazorPagesMovie’一起。最終形成頁面上我們看到的標題:
④ Razor頁面的註釋方式採用: @* 註釋內容*@ 的方式進行註釋(區別於HTML的註釋<!-- 註釋-- >)。註釋不會被髮送到客戶端
5、更新佈局
① 更改Title和鏈接頁面
/Pages/Shared/_Layout.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"] - 電影</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</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">
<a class="navbar-brand" asp-page="/Movies/Index">我的電影</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
這裡一共更改了2處:
<title>@ViewData["Title"] - 電影</title>
<a class="navbar-brand" asp-page="/Movies/Index">我的電影</a>
其中第二處更改,原先的代碼是:asp-area="" asp-page="/Movies/Index">,它表示一個標記幫助程式。更改之後,它是一個定位點標記幫助程式。asp-page="/Movies/Index"標記了幫助程式的屬性和值可以創建指向/Movies/Index的Razor頁面的連接。其中,asp-area屬性值為空,表示連接中未使用區域。
Pages/Movies/Index.cshtml文件:
@page
@model RazorPagesMovie.IndexModel
@{
ViewData["Title"] = "首頁";
}
<h1>首頁</h1>
Pages/_ViewStart.cshtml中設置Layout屬性:
1 @{ 2 Layout = "_Layout"; 3 }
這個標記,針對所有Razor文件將佈局文件設置為Pages文件夾下的Pages/Shared/_Layout.cshtml。
Pages/Movies/Create.cshtml.cs文件:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 using Microsoft.AspNetCore.Mvc.RazorPages; 7 using Microsoft.AspNetCore.Mvc.Rendering; 8 using RazorPagesMovie.Data; 9 using RazorPagesMovie.Models; 10 11 namespace RazorPagesMovie 12 { 13 public class CreateModel : PageModel 14 { 15 private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context; 16 17 public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context) 18 { 19 _context = context; 20 } 21 22 public IActionResult OnGet() 23 { 24 return Page(); 25 } 26 27 [BindProperty] 28 public Movie Movie { get; set; } 29 30 // To protect from overposting attacks, please enable the specific properties you want to bind to, for 31 // more details see https://aka.ms/RazorPagesCRUD. 32 public async Task<IActionResult> OnPostAsync() 33 { 34 if (!ModelState.IsValid) 35 { 36 return Page(); 37 } 38 39 _context.Movie.Add(Movie); 40 await _context.SaveChangesAsync(); 41 42 return RedirectToPage("./Index"); 43 } 44 } 45 }
第22行:OnGet方法初始化頁面所需的任何狀態。Create頁沒有任何要初始化的狀態,因此返回Page(),Page()方法創建用於呈現Create.cshtml頁的PageResult對象。後面,我們會繼續學習OnGet初始化狀態的示例。
第27、28行:使用[BindProperty]特性,來給Movie屬性選擇加入模型綁定。當Create頁面發佈表單值(form 標記)時,ASP.NET Core運行時將發佈(post回傳)的值綁定到Movie模型。
第32行:當頁面發佈(post)表單(form)數據時,將運行OnGetAsync方法。
第34-37行:如果不存在任何模型錯誤,將重新顯示表單,以及post回的任何表單數據。在post回form前,在可以在客戶端捕獲到大部分的模型錯誤。模型錯誤的一個實例是:post回的日期欄位值無法轉換為日期。
第39-42行:如果不存在模型錯誤,將保存數據。最後瀏覽器會重定向到Index頁面。
打開、更新Pages/Movies/Create.cshtml 的Razor頁面:
1 @page 2 @model RazorPagesMovie.CreateModel 3 4 @{ 5 ViewData["Title"] = "添加"; 6 } 7 8 <h1>新增</h1> 9 10 <h4>電影</h4> 11 <hr /> 12 <div class="row"> 13 <div class="col-md-4"> 14 <form method="post"> 15 <div asp-validation-summary="ModelOnly" class="text-danger"></div> 16 <div class="form-group"> 17 <label asp-for="Movie.Title" class="control-label">標題</label> 18 <input asp-for="Movie.Title" class="form-control" /> 19 <span asp-validation-for="Movie.Title" class="text-danger"></span> 20 </div> 21 <div class="form-group"> 22 <label asp-for="Movie.ReleaseDate" class="control-label">發佈時間</label> 23 <input asp-for="Movie.ReleaseDate" class="form-control" /> 24 <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span> 25 </div> 26 <div class="form-group"> 27 <label asp-for="Movie.Genre" class="control-label">題材</label> 28 <input asp-for="Movie.Genre" class="form-control" /> 29 <span asp-validation-for="Movie.Genre" class="text-danger"></span> 30 </div> 31 <div class="form-group"> 32 <label asp-for="Movie.Price" class="control-label">價格</label> 33 <input asp-for="Movie.Price" class="form-control" /> 34 <span asp-validation-for="Movie.Price" class="text-danger"></span> 35 </div> 36 <div class="form-group"> 37 <input type="submit" value="添加" class="btn btn-primary" /> 38 </div> 39 </form> 40 </div> 41 </div> 42 43 <div> 44 <a asp-page="Index">返回到電影列表</a> 45 </div> 46 47 @section Scripts { 48 @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 49 }
第14行:表示這個元素是一個表單標記幫助程式(form tag helper)。表單標記幫助程式會自動包含防偽令牌(antiforgery token)。
第15-34行:基架引擎在模型中為每個欄位(除ID外)創建Razor標記。
其中<div asp-validation-summary>和<span asp-validation-for>一起,用於顯示驗證錯誤。詳細的驗證信息以後再學習。
<label asp-for="" class="">是標簽標記幫助程式(label tag helper)。生成一個標簽描述和Title屬性的for特性。(這裡我們手動更改為“標題”)。
<input asp_for="" class="">使用DataAnnotations屬性併在客戶端生成jQuery驗證所需的HTML屬性。
6、驗證
按下ctrl+F5,運行應用程式。測試我們更改的效果。