第 3 部分,ASP.NET Core 中的 Scaffold Razor 頁面

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

作者:Rick Anderson

本教學課程會檢查在先前教學課程中 Scaffolding 所建立的 Razor 頁面。

Create、Delete、Details 和 Edit 頁面

檢查 Pages/Movies/Index.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor 頁面衍生自 PageModel。 依照慣例,PageModel 衍生類別會命名為 PageNameModel。 例如,[索引] 頁面名為 IndexModel

建構函式會使用相依性插入 ,將 RazorPagesMovieContext 新增至頁面:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

如需使用 Entity Framework 進行非同步程式設計的詳細資訊,請參閱非同步程式碼

當針對頁面提出 GET 要求時,OnGetAsync 方法會將電影清單傳回 Razor 頁面。 在 Razor 頁面上,呼叫 OnGetAsyncOnGet 以初始化頁面的狀態。 在此情況下,OnGetAsync 會取得電影清單並加以顯示。

OnGet 傳回 voidOnGetAsync 傳回 Task 時,並未使用任何 return 陳述式。 例如,檢查 Privacy 頁面:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

當傳回型別是 IActionResultTask<IActionResult> 時,必須提供傳回陳述式。 例如,Pages/Movies/Create.cshtml.cs OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

檢查 Pages/Movies/Index.cshtmlRazor 頁面:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor 可以從 HTML 轉換成 C# 或 Razor 特定標記。 當 @ 符號後面接著 Razor 保留關鍵字時,它會轉換成 Razor 特定標記,否則就會轉換成 C#。

@page 指示詞

@pageRazor 指示詞可讓檔案成為 MVC 動作,這表示它可以處理要求。 @page 必須是頁面上的第一個 Razor 指示詞。 @page@model 是轉換成 Razor 特定標記的範例。 如需詳細資訊,請參閱 Razor 語法

@model 指示詞

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model 指示詞會指定傳遞至 Razor 頁面的模型類型。 在上述範例中,@model 行可讓 PageModel 衍生的類別供 Razor 頁面使用。 此模型用於頁面上的 @Html.DisplayNameFor@Html.DisplayForHTML 協助程式

檢查下列 HTML 協助程式中使用的 Lambda 運算式:

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML 協助程式會檢查 Lambda 運算式中參考的 Title 屬性來判斷顯示名稱。 Lambda 運算式是進行檢查而不是評估。 這表示當 modelmodel.Moviemodel.Movie[0]null 或空白時,不會有任何存取違規。 在評估 Lambda 運算式時 (例如,使用 @Html.DisplayFor(modelItem => item.Title)),會評估模型的屬性值。

版面配置頁

選取功能表連結 RazorPagesMovieHomePrivacy。 每個頁面會顯示相同的功能表配置。 功能表版面配置要在 Pages/Shared/_Layout.cshtml 檔案中實作。

開啟並檢查 Pages/Shared/_Layout.cshtml 檔案。

版面配置範本可讓 HTML 容器版面配置:

  • 指定在一個位置。
  • 套用於網站中的多個頁面。

找到 @RenderBody() 這行。 RenderBody 是一個預留位置,可供顯示所有頁面特定檢視 (「包裝」在版面配置頁面中)。 例如,選取 Privacy 連結,則 Pages/Privacy.cshtml 檢視就會呈現在 RenderBody 方法內。

ViewData 和 Layout

請考慮 Pages/Movies/Index.cshtml 檔案中的下列標記:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上述強調顯示的標記是 Razor 轉換成 C# 的範例。 {} 字元中含括 C# 程式碼的區塊。

PageModel 基底類別包含 ViewData 字典屬性,可用來新增資料並傳遞到檢視。 物件會使用機碼/值模式新增至 ViewData 字典。 在上述範例中,Title 屬性會新增至 ViewData 字典。

Title 屬性會用於 Pages/Shared/_Layout.cshtml 檔案中。 下列標記會顯示 _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"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

更新配置

  1. 變更 Pages/Shared/_Layout.cshtml 檔案中的 <title> 項目,以顯示 Movie 而不是 RazorRazorPagesMovie

    <!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</title>
    
  2. Pages/Shared/_Layout.cshtml 檔案中尋找下列錨點項目。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 以下列標記來取代上述元素:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上述的錨點項目是標記協助程式。 在此情況下,它是錨點標記協助程式asp-page="/Movies/Index" 標記協助程式的屬性和值會建立 /Movies/IndexRazor 頁面的連結。 asp-area 屬性值為空白,因此不會在連結中使用該區域。 如需詳細資訊,請參閱區域

  4. 選取 RpMovie 連結來儲存變更並測試應用程式。 如有任何問題,請參閱 GitHub 中的 _Layout.cshtml 檔案。

  5. 測試 HomeRpMovie建立編輯,以及刪除連結。 每一頁都會設定標題,您可以在瀏覽器索引標籤中看到該標題。當您將某個頁面加為書籤時,會使用標題來表示書籤。

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

Layout 屬性是在 Pages/_ViewStart.cshtml 檔案中設定:

@{
    Layout = "_Layout";
}

上述的標記會針對 Pages 資料夾下的所有 Razor 檔案.將配置檔設定為 Pages/Shared/_Layout.cshtml。 如需詳細資訊,請參閱 Layout

Create 頁面模型

檢查 Pages/Movies/Create.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet 方法會初始化頁面所需的任何狀態。 建立頁面沒有任何要初始化的狀態,所以傳回 Page。 稍後在此教學課程中,會顯示 OnGet 初始化狀態的範例。 Page 方法會建立 PageResult 物件,用以呈現 Create.cshtml 頁面。

Movie 屬性 (property) 使用 [BindProperty] 屬性 (attribute) 來加入模型繫結。 當 Create 表單發佈表單值時,ASP.NET Core 執行階段會將發佈的值繫結至 Movie 模型。

當頁面發佈表單資料時,即會執行 OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

如果沒有任何模型錯誤,將會重新顯示表單,以及任何發佈的表單資料。 大部分的模型錯誤可以在發佈表單之前,於用戶端上攔截到。 模型錯誤的範例為針對日期欄位發佈無法轉換為日期的值。 稍後的教學課程中將討論用戶端驗證和模型驗證。

如果沒有模型錯誤:

  • 資料會儲存。
  • 瀏覽器會重新導向至 [索引] 頁面。

建立 Razor 頁面

檢查 Pages/Movies/Create.cshtmlRazor 頁面檔案:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Visual Studio 會以用於標籤協助程式的特殊粗體字型顯示下列標籤:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml 頁面的 VS17 檢視

<form method="post"> 項目是表單標記協助程式。 表單標記協助程式會自動包含 antiforgery 語彙基元

Scaffolding 引擎會在模型中建立每個欄位的 Razor 標記 (除了識別碼),如下所示:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

驗證標記協助程式 (<div asp-validation-summary<span asp-validation-for) 會顯示驗證錯誤。 驗證將於本文稍後詳細討論到。

標籤標記協助程式 (<label asp-for="Movie.Title" class="control-label"></label>) 會產生 Title 屬性 (property) 的標籤標題和 [for] 屬性 (attribute)。

輸入標記協助程式 (<input asp-for="Movie.Title" class="form-control">) 會使用DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。

如需標籤協助程式 (例如 <form method="post">) 的詳細資訊,請參閱 ASP.NET Core 中的標籤協助程式

下一步

Create、Delete、Details 和 Edit 頁面

檢查 Pages/Movies/Index.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor 頁面衍生自 PageModel。 依照慣例,PageModel 衍生類別會命名為 PageNameModel。 例如,[索引] 頁面名為 IndexModel

建構函式會使用相依性插入 ,將 RazorPagesMovieContext 新增至頁面:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

如需使用 Entity Framework 進行非同步程式設計的詳細資訊,請參閱非同步程式碼

當針對頁面提出 GET 要求時,OnGetAsync 方法會將電影清單傳回 Razor 頁面。 在 Razor 頁面上,呼叫 OnGetAsyncOnGet 以初始化頁面的狀態。 在此情況下,OnGetAsync 會取得電影清單並加以顯示。

OnGet 傳回 voidOnGetAsync 傳回 Task 時,並未使用任何 return 陳述式。 例如,檢查 Privacy 頁面:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

當傳回型別是 IActionResultTask<IActionResult> 時,必須提供傳回陳述式。 例如,Pages/Movies/Create.cshtml.cs OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

檢查 Pages/Movies/Index.cshtmlRazor 頁面:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor 可以從 HTML 轉換成 C# 或 Razor 特定標記。 當 @ 符號後面接著 Razor 保留關鍵字時,它會轉換成 Razor 特定標記,否則就會轉換成 C#。

@page 指示詞

@pageRazor 指示詞可讓檔案成為 MVC 動作,這表示它可以處理要求。 @page 必須是頁面上的第一個 Razor 指示詞。 @page@model 是轉換成 Razor 特定標記的範例。 如需詳細資訊,請參閱 Razor 語法

@model 指示詞

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model 指示詞會指定傳遞至 Razor 頁面的模型類型。 在上述範例中,@model 行可讓 PageModel 衍生的類別供 Razor 頁面使用。 此模型用於頁面上的 @Html.DisplayNameFor@Html.DisplayForHTML 協助程式

檢查下列 HTML 協助程式中使用的 Lambda 運算式:

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML 協助程式會檢查 Lambda 運算式中參考的 Title 屬性來判斷顯示名稱。 Lambda 運算式是進行檢查而不是評估。 這表示當 modelmodel.Moviemodel.Movie[0]null 或空白時,不會有任何存取違規。 在評估 Lambda 運算式時 (例如,使用 @Html.DisplayFor(modelItem => item.Title)),會評估模型的屬性值。

版面配置頁

選取功能表連結 RazorPagesMovieHomePrivacy。 每個頁面會顯示相同的功能表配置。 功能表版面配置要在 Pages/Shared/_Layout.cshtml 檔案中實作。

開啟並檢查 Pages/Shared/_Layout.cshtml 檔案。

版面配置範本可讓 HTML 容器版面配置:

  • 指定在一個位置。
  • 套用於網站中的多個頁面。

找到 @RenderBody() 這行。 RenderBody 是一個預留位置,可供顯示所有頁面特定檢視 (「包裝」在版面配置頁面中)。 例如,選取 Privacy 連結,則 Pages/Privacy.cshtml 檢視就會呈現在 RenderBody 方法內。

ViewData 和 Layout

請考慮 Pages/Movies/Index.cshtml 檔案中的下列標記:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上述強調顯示的標記是 Razor 轉換成 C# 的範例。 {} 字元中含括 C# 程式碼的區塊。

PageModel 基底類別包含 ViewData 字典屬性,可用來新增資料並傳遞到檢視。 物件會使用機碼/值模式新增至 ViewData 字典。 在上述範例中,Title 屬性會新增至 ViewData 字典。

Title 屬性會用於 Pages/Shared/_Layout.cshtml 檔案中。 下列標記會顯示 _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"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

@*Markup removed for brevity.*@ 是 Razor 註解。 不同於 HTML 註解 <!-- -->,Razor 註解不會傳送到用戶端。 如需詳細資訊,請參閱 MDN Web 文件:開始使用 HTML

更新配置

  1. 變更 Pages/Shared/_Layout.cshtml 檔案中的 <title> 項目,以顯示 Movie 而不是 RazorRazorPagesMovie

    <!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</title>
    
  2. Pages/Shared/_Layout.cshtml 檔案中尋找下列錨點項目。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 以下列標記來取代上述元素:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上述的錨點項目是標記協助程式。 在此情況下,它是錨點標記協助程式asp-page="/Movies/Index" 標記協助程式的屬性和值會建立 /Movies/IndexRazor 頁面的連結。 asp-area 屬性值為空白,因此不會在連結中使用該區域。 如需詳細資訊,請參閱區域

  4. 選取 RpMovie 連結來儲存變更並測試應用程式。 如有任何問題,請參閱 GitHub 中的 _Layout.cshtml 檔案。

  5. 測試 HomeRpMovie建立編輯,以及刪除連結。 每一頁都會設定標題,您可以在瀏覽器索引標籤中看到該標題。當您將某個頁面加為書籤時,會使用標題來表示書籤。

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

Layout 屬性是在 Pages/_ViewStart.cshtml 檔案中設定:

@{
    Layout = "_Layout";
}

上述的標記會針對 Pages 資料夾下的所有 Razor 檔案.將配置檔設定為 Pages/Shared/_Layout.cshtml。 如需詳細資訊,請參閱 Layout

Create 頁面模型

檢查 Pages/Movies/Create.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet 方法會初始化頁面所需的任何狀態。 建立頁面沒有任何要初始化的狀態,所以傳回 Page。 稍後在此教學課程中,會顯示 OnGet 初始化狀態的範例。 Page 方法會建立 PageResult 物件,用以呈現 Create.cshtml 頁面。

Movie 屬性 (property) 使用 [BindProperty] 屬性 (attribute) 來加入模型繫結。 當 Create 表單發佈表單值時,ASP.NET Core 執行階段會將發佈的值繫結至 Movie 模型。

當頁面發佈表單資料時,即會執行 OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

如果沒有任何模型錯誤,將會重新顯示表單,以及任何發佈的表單資料。 大部分的模型錯誤可以在發佈表單之前,於用戶端上攔截到。 模型錯誤的範例為針對日期欄位發佈無法轉換為日期的值。 稍後的教學課程中將討論用戶端驗證和模型驗證。

如果沒有模型錯誤:

  • 資料會儲存。
  • 瀏覽器會重新導向至 [索引] 頁面。

建立 Razor 頁面

檢查 Pages/Movies/Create.cshtmlRazor 頁面檔案:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Visual Studio 會以用於標籤協助程式的特殊粗體字型顯示下列標籤:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml 頁面的 VS17 檢視

<form method="post"> 項目是表單標記協助程式。 表單標記協助程式會自動包含 antiforgery 語彙基元

Scaffolding 引擎會在模型中建立每個欄位的 Razor 標記 (除了識別碼),如下所示:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

驗證標記協助程式 (<div asp-validation-summary<span asp-validation-for) 會顯示驗證錯誤。 驗證將於本文稍後詳細討論到。

標籤標記協助程式 (<label asp-for="Movie.Title" class="control-label"></label>) 會產生 Title 屬性 (property) 的標籤標題和 [for] 屬性 (attribute)。

輸入標記協助程式 (<input asp-for="Movie.Title" class="form-control">) 會使用DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。

如需標籤協助程式 (例如 <form method="post">) 的詳細資訊,請參閱 ASP.NET Core 中的標籤協助程式

下一步

Create、Delete、Details 和 Edit 頁面

檢查 Pages/Movies/Index.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            if (_context.Movie != null)
            {
                Movie = await _context.Movie.ToListAsync();
            }
        }
    }
}

Razor 頁面衍生自 PageModel。 依照慣例,PageModel 衍生類別會命名為 PageNameModel。 例如,[索引] 頁面名為 IndexModel

建構函式會使用相依性插入 ,將 RazorPagesMovieContext 新增至頁面:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

如需使用 Entity Framework 進行非同步程式設計的詳細資訊,請參閱非同步程式碼

當針對頁面提出要求時,OnGetAsync 方法會將電影清單傳回 Razor 頁面。 在 Razor 頁面上,呼叫 OnGetAsyncOnGet 以初始化頁面的狀態。 在此情況下,OnGetAsync 會取得電影清單並加以顯示。

OnGet 傳回 voidOnGetAsync 傳回 Task 時,並未使用任何 return 陳述式。 例如,檢查 Privacy 頁面:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

當傳回型別是 IActionResultTask<IActionResult> 時,必須提供傳回陳述式。 例如,Pages/Movies/Create.cshtml.csOnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

檢查 Pages/Movies/Index.cshtmlRazor 頁面:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor 可以從 HTML 轉換成 C# 或 Razor 特定標記。 當 @ 符號後面接著 Razor 保留關鍵字時,它會轉換成 Razor 特定標記,否則就會轉換成 C#。

@page 指示詞

@pageRazor 指示詞可讓檔案成為 MVC 動作,這表示它可以處理要求。 @page 必須是頁面上的第一個 Razor 指示詞。 @page@model 是轉換成 Razor 特定標記的範例。 如需詳細資訊,請參閱 Razor 語法

@model 指示詞

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model 指示詞會指定傳遞至 Razor 頁面的模型類型。 在上述範例中,@model 行可讓 PageModel 衍生的類別供 Razor 頁面使用。 此模型用於頁面上的 @Html.DisplayNameFor@Html.DisplayForHTML 協助程式

檢查下列 HTML 協助程式中使用的 Lambda 運算式:

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML 協助程式會檢查 Lambda 運算式中參考的 Title 屬性來判斷顯示名稱。 Lambda 運算式是進行檢查而不是評估。 這表示當 modelmodel.Moviemodel.Movie[0]null 或空白時,不會有任何存取違規。 在評估 Lambda 運算式時 (例如,使用 @Html.DisplayFor(modelItem => item.Title)),會評估模型的屬性值。

版面配置頁

選取功能表連結 RazorPagesMovieHomePrivacy。 每個頁面會顯示相同的功能表配置。 功能表版面配置要在 Pages/Shared/_Layout.cshtml 檔案中實作。

開啟並檢查 Pages/Shared/_Layout.cshtml 檔案。

版面配置範本可讓 HTML 容器版面配置:

  • 指定在一個位置。
  • 套用於網站中的多個頁面。

找到 @RenderBody() 這行。 RenderBody 是一個預留位置,可供顯示所有頁面特定檢視 (「包裝」在版面配置頁面中)。 例如,選取 Privacy 連結,則 Pages/Privacy.cshtml 檢視就會呈現在 RenderBody 方法內。

ViewData 和 Layout

請考慮 Pages/Movies/Index.cshtml 檔案中的下列標記:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上述強調顯示的標記是 Razor 轉換成 C# 的範例。 {} 字元中含括 C# 程式碼的區塊。

PageModel 基底類別包含 ViewData 字典屬性,可用來新增資料並傳遞到檢視。 物件會使用機碼/值模式新增至 ViewData 字典。 在上述範例中,Title 屬性會新增至 ViewData 字典。

Title 屬性會用於 Pages/Shared/_Layout.cshtml 檔案中。 下列標記會顯示 _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"] - RazorPagesMovie</title>

     @*Markup removed for brevity.*@
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

@*Markup removed for brevity.*@ 是 Razor 註解。 不同於 HTML 註解 <!-- -->,Razor 註解不會傳送到用戶端。 如需詳細資訊,請參閱 MDN Web 文件:開始使用 HTML

更新配置

  1. 變更 Pages/Shared/_Layout.cshtml 檔案中的 <title> 項目,以顯示 Movie 而不是 RazorRazorPagesMovie

    <!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</title>
    
  2. Pages/Shared/_Layout.cshtml 檔案中尋找下列錨點項目。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 以下列標記來取代上述元素:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上述的錨點項目是標記協助程式。 在此情況下,它是錨點標記協助程式asp-page="/Movies/Index" 標記協助程式的屬性和值會建立 /Movies/IndexRazor 頁面的連結。 asp-area 屬性值為空白,因此不會在連結中使用該區域。 如需詳細資訊,請參閱區域

  4. 選取 RpMovie 連結來儲存變更並測試應用程式。 如有任何問題,請參閱 GitHub 中的 _Layout.cshtml 檔案。

  5. 測試 HomeRpMovie建立編輯,以及刪除連結。 每一頁都會設定標題,您可以在瀏覽器索引標籤中看到該標題。當您將某個頁面加為書籤時,會使用標題來表示書籤。

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

Layout 屬性是在 Pages/_ViewStart.cshtml 檔案中設定:

@{
    Layout = "_Layout";
}

上述的標記會針對 Pages 資料夾下的所有 Razor 檔案.將配置檔設定為 Pages/Shared/_Layout.cshtml。 如需詳細資訊,請參閱 Layout

Create 頁面模型

檢查 Pages/Movies/Create.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet 方法會初始化頁面所需的任何狀態。 建立頁面沒有任何要初始化的狀態,所以傳回 Page。 稍後在此教學課程中,會顯示 OnGet 初始化狀態的範例。 Page 方法會建立 PageResult 物件,用以呈現 Create.cshtml 頁面。

Movie 屬性 (property) 使用 [BindProperty] 屬性 (attribute) 來加入模型繫結。 當 Create 表單發佈表單值時,ASP.NET Core 執行階段會將發佈的值繫結至 Movie 模型。

當頁面發佈表單資料時,即會執行 OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

如果沒有任何模型錯誤,將會重新顯示表單,以及任何發佈的表單資料。 大部分的模型錯誤可以在發佈表單之前,於用戶端上攔截到。 模型錯誤的範例為針對日期欄位發佈無法轉換為日期的值。 稍後的教學課程中將討論用戶端驗證和模型驗證。

如果沒有模型錯誤:

  • 資料會儲存。
  • 瀏覽器會重新導向至 [索引] 頁面。

建立 Razor 頁面

檢查 Pages/Movies/Create.cshtmlRazor 頁面檔案:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Visual Studio 會以用於標籤協助程式的特殊粗體字型顯示下列標籤:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml 頁面的 VS17 檢視

<form method="post"> 項目是表單標記協助程式。 表單標記協助程式會自動包含 antiforgery 語彙基元

Scaffolding 引擎會在模型中建立每個欄位的 Razor 標記 (除了識別碼),如下所示:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

驗證標記協助程式 (<div asp-validation-summary<span asp-validation-for) 會顯示驗證錯誤。 驗證將於本文稍後詳細討論到。

標籤標記協助程式 (<label asp-for="Movie.Title" class="control-label"></label>) 會產生 Title 屬性 (property) 的標籤標題和 [for] 屬性 (attribute)。

輸入標記協助程式 (<input asp-for="Movie.Title" class="form-control">) 會使用DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。

如需標籤協助程式 (例如 <form method="post">) 的詳細資訊,請參閱 ASP.NET Core 中的標籤協助程式

下一步

Create、Delete、Details 和 Edit 頁面

檢查 Pages/Movies/Index.cshtml.cs 頁面模型:

// Unused usings removed.
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }
        public IList<Movie> Movie { get;set; }

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor 頁面衍生自 PageModel。 依照慣例,PageModel 衍生類別會命名為 <PageName>Model。 建構函式會使用相依性插入 ,將 RazorPagesMovieContext 新增至頁面:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

如需使用 Entity Framework 進行非同步程式設計的詳細資訊,請參閱非同步程式碼

當針對頁面提出要求時,OnGetAsync 方法會將電影清單傳回 Razor 頁面。 在 Razor 頁面上,呼叫 OnGetAsyncOnGet 以初始化頁面的狀態。 在此情況下,OnGetAsync 會取得電影清單並加以顯示。

OnGet 傳回 voidOnGetAsync 傳回 Task 時,並未使用任何 return 陳述式。 例如,Privacy 頁面:

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}

當傳回型別是 IActionResultTask<IActionResult> 時,必須提供傳回陳述式。 例如,Pages/Movies/Create.cshtml.csOnPostAsync 方法:

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Movie.Add(Movie);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

檢查 Pages/Movies/Index.cshtmlRazor 頁面:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor 可以從 HTML 轉換成 C# 或 Razor 特定標記。 當 @ 符號後面接著 Razor 保留關鍵字時,它會轉換成 Razor 特定標記,否則就會轉換成 C#。

@page 指示詞

@pageRazor 指示詞可讓檔案成為 MVC 動作,這表示它可以處理要求。 @page 必須是頁面上的第一個 Razor 指示詞。 @page@model 是轉換成 Razor 特定標記的範例。 如需詳細資訊,請參閱 Razor 語法

@model 指示詞

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

@model 指示詞會指定傳遞至 Razor 頁面的模型類型。 在上述範例中,@model 行可讓 PageModel 衍生的類別供 Razor 頁面使用。 此模型用於頁面上的 @Html.DisplayNameFor@Html.DisplayForHTML 協助程式

檢查下列 HTML 協助程式中使用的 Lambda 運算式:

@Html.DisplayNameFor(model => model.Movie[0].Title)

DisplayNameFor HTML 協助程式會檢查 Lambda 運算式中參考的 Title 屬性來判斷顯示名稱。 Lambda 運算式是進行檢查而不是評估。 這表示當 modelmodel.Moviemodel.Movie[0]null 或空白時,不會有任何存取違規。 在評估 Lambda 運算式時 (例如,使用 @Html.DisplayFor(modelItem => item.Title)),會評估模型的屬性值。

版面配置頁

選取功能表連結 RazorPagesMovieHomePrivacy。 每個頁面會顯示相同的功能表配置。 功能表版面配置要在 Pages/Shared/_Layout.cshtml 檔案中實作。

開啟並檢查 Pages/Shared/_Layout.cshtml 檔案。

版面配置範本可讓 HTML 容器版面配置:

  • 指定在一個位置。
  • 套用於網站中的多個頁面。

找到 @RenderBody() 這行。 RenderBody 是一個預留位置,可供顯示所有頁面特定檢視 (「包裝」在版面配置頁面中)。 例如,選取 Privacy 連結,則 Pages/Privacy.cshtml 檢視就會呈現在 RenderBody 方法內。

ViewData 和 Layout

請考慮 Pages/Movies/Index.cshtml 檔案中的下列標記:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

上述強調顯示的標記是 Razor 轉換成 C# 的範例。 {} 字元中含括 C# 程式碼的區塊。

PageModel 基底類別包含 ViewData 字典屬性,可用來新增資料並傳遞到檢視。 物件會使用機碼/值模式新增至 ViewData 字典。 在上述範例中,Title 屬性會新增至 ViewData 字典。

Title 屬性會用於 Pages/Shared/_Layout.cshtml 檔案中。 下列標記會顯示 _Layout.cshtml 檔案的前幾行。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>

    @*Markup removed for brevity.*@

@*Markup removed for brevity.*@ 是 Razor 註解。 不同於 HTML 註解 <!-- -->,Razor 註解不會傳送到用戶端。 如需詳細資訊,請參閱 MDN Web 文件:開始使用 HTML

更新配置

  1. 變更 Pages/Shared/_Layout.cshtml 檔案中的 <title> 項目,以顯示 Movie 而不是 RazorRazorPagesMovie

    <!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</title>
    
  2. Pages/Shared/_Layout.cshtml 檔案中尋找下列錨點項目。

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. 以下列標記來取代上述元素:

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    上述的錨點項目是標記協助程式。 在此情況下,它是錨點標記協助程式asp-page="/Movies/Index" 標記協助程式的屬性和值會建立 /Movies/IndexRazor 頁面的連結。 asp-area 屬性值為空白,因此不會在連結中使用該區域。 如需詳細資訊,請參閱區域

  4. 選取 RpMovie 連結來儲存變更並測試應用程式。 如有任何問題,請參閱 GitHub 中的 _Layout.cshtml 檔案。

  5. 測試 HomeRpMovie建立編輯,以及刪除連結。 每一頁都會設定標題,您可以在瀏覽器索引標籤中看到該標題。當您將某個頁面加為書籤時,會使用標題來表示書籤。

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

Layout 屬性是在 Pages/_ViewStart.cshtml 檔案中設定:

@{
    Layout = "_Layout";
}

上述的標記會針對 Pages 資料夾下的所有 Razor 檔案.將配置檔設定為 Pages/Shared/_Layout.cshtml。 如需詳細資訊,請參閱 Layout

Create 頁面模型

檢查 Pages/Movies/Create.cshtml.cs 頁面模型:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;
using System;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        public Movie Movie { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

OnGet 方法會初始化頁面所需的任何狀態。 建立頁面沒有任何要初始化的狀態,所以傳回 Page。 稍後在此教學課程中,會顯示 OnGet 初始化狀態的範例。 Page 方法會建立 PageResult 物件,用以呈現 Create.cshtml 頁面。

Movie 屬性 (property) 使用 [BindProperty] 屬性 (attribute) 來加入模型繫結。 當 Create 表單發佈表單值時,ASP.NET Core 執行階段會將發佈的值繫結至 Movie 模型。

當頁面發佈表單資料時,即會執行 OnPostAsync 方法:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

如果沒有任何模型錯誤,將會重新顯示表單,以及任何發佈的表單資料。 大部分的模型錯誤可以在發佈表單之前,於用戶端上攔截到。 模型錯誤的範例為針對日期欄位發佈無法轉換為日期的值。 稍後的教學課程中將討論用戶端驗證和模型驗證。

如果沒有模型錯誤:

  • 資料會儲存。
  • 瀏覽器會重新導向至 [索引] 頁面。

建立 Razor 頁面

檢查 Pages/Movies/Create.cshtmlRazor 頁面檔案:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Visual Studio 會以用於標籤協助程式的特殊粗體字型顯示下列標籤:

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Create.cshtml 頁面的 VS17 檢視

<form method="post"> 項目是表單標記協助程式。 表單標記協助程式會自動包含 antiforgery 語彙基元

Scaffolding 引擎會在模型中建立每個欄位的 Razor 標記 (除了識別碼),如下所示:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

驗證標記協助程式 (<div asp-validation-summary<span asp-validation-for) 會顯示驗證錯誤。 驗證將於本文稍後詳細討論到。

標籤標記協助程式 (<label asp-for="Movie.Title" class="control-label"></label>) 會產生 Title 屬性 (property) 的標籤標題和 [for] 屬性 (attribute)。

輸入標記協助程式 (<input asp-for="Movie.Title" class="form-control">) 會使用DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。

如需標籤協助程式 (例如 <form method="post">) 的詳細資訊,請參閱 ASP.NET Core 中的標籤協助程式

下一步