7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ASP.NET Core 3.0 Razor Pages 事始め(4) - ページモデルとページハンドラ

Last updated at Posted at 2019-10-30

記事目次

  1. ASP.NET Core 3.0 Razor Pages 事始め(1) - はじめてのRazor Pagesアプリケーション
  2. ASP.NET Core 3.0 Razor Pages 事始め(2) - スキャフォールディングとDBマイグレーション
  3. ASP.NET Core 3.0 Razor Pages 事始め(3) - マイグレーションのやり直しとURLルーティング
  4. ASP.NET Core 3.0 Razor Pages 事始め(4) - ページモデルとページハンドラ <-- この記事
  5. ASP.NET Core 3.0 Razor Pages 事始め(5) - Postページハンドラとタグヘルパー
  6. ASP.NET Core 3.0 Razor Pages 事始め(6) - データベースに初期値を設定する
  7. ASP.NET Core 3.0 Razor Pages 事始め(7) - Viewの変更とコンカレンシー例外処理
  8. ASP.NET Core 3.0 Razor Pages 事始め(8) - 検索機能の追加
  9. ASP.NET Core 3.0 Razor Pages 事始め(9) - ページに新しいフィールドを追加する
  10. ASP.NET Core 3.0 Razor Pages 事始め(10) - 検証機能の追加

前回ASP.NET Core 3.0 Razor Pages 事始め(3)の続きです。

今回は、前々回スキャフォールディング機能によって作成されたRazorページについて、見ていこうと思います。

Index.cshtml/ Index.cshtml.cs

Movies/Index.cshtml.cs と Movies/Index.cshtmlを見ていきす。

Index.cshtml.cs

まずは、C#のコードから

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages_Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Models.RazorPagesMovieContext _context;

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

        public IList<Movie> Movies { get;set; }

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

PageModelクラスを継承したIndexModelというクラスが定義されています。このクラスをページモデルと言うようです。

誤解を恐れずに言えば、一つのページ(ここでは、Index.cshtml)に特化したControllerクラスといったらよいでしょうか? 
さらに、ViewModelクラスの特徴も持ち合わせているようなクラスです。

コンストラクタ

コンストラクタでは、DIを使用して、RazorPagesMovie.Models.RazorPagesMovieContext のインスタンスが引数に渡されます。
これを、_context フィールドに格納しています。

これで、ページモデルでデータベースにアクセスすることが可能になります。

ページハンドラ

OnGetAsyncメソッドが、ブラウザからのリクエストに対応するメソッドです。MVCでいうActionメソッドのようなものですね。これをページハンドラと呼ぶようです。

OnGetAsyncという名前から、Http Get要求に対応するメソッドだとわかります。

非同期メソッドにしない場合は、単に OnGetという名前にします。が、あえて、同期にする必要はないですね。

このハンドラーが呼び出されると、EF Core使って、データベースから Movieのリストを取得して、Moviesプロパティに格納しています。

戻り値は、TaskOnGetの場合はvoid)ですが、Task<IActionResult>IActionResult を返すこともできます。

例えば、別のページに遷移させたい場合は

return RedirectToPage("/Moveis/Details");

などと書けます。RedirectToAction ではなく、RedirectToPage です。

Index.cshtml

次は、このページモデルに対応する ページ(cshtml)ファイルを確認します。

@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.Movies[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movies[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movies) {
        <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構文です。

先頭の@pageは、Razorページであることを示しています。

@model RazorPagesMovie.Pages.Movies.IndexModel

は、ページで利用するモデルの型を指定します。ここでは、IndexModelクラスを指定しています。

あと特徴的なのは、以下のようにDisplayNameForを使っている行ですね。

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

@Html.で利用できるメソッドをHTMLヘルパーと呼んでいます。DisplayNameForはその一つです。ASP.NET MVCと同じですね。

このラムダ式に渡ってくるのが、2行目の@modelで指定した IndexModelクラスのインスタンスです。
DisplayNameFor では、通常のC#と違って、値がnullであっても、Moviesの要素が空であっても例外は発生しません。

@foreach文の中では、

@Html.DisplayFor(modelItem => item.Title)

という行があります。ラムダ式の引き数modeiItemは、IndexModelオブジェクトなので、ここでは利用できません。そのため、@foreachで列挙した item変数を使って、それぞれの Movieオブジェクトにアクセスしています。かなり違和感がある書き方ですが、まあそういうものだと思うしかないですね。

ViewData プロパティ

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

ViewDataは、ページモデル(具体的には、基底クラスであるPageModelクラスに定義されているディクショナリプロパティです。
この ViewData["Title"]の値は、Pages/Shared/_Layout.cshtml で利用されます。

_Layout.cshtml

次に、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"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>

先頭部分だけ抜きだしてみました。

この<title>要素で、先ほどのViewData["Title"]の値を参照しています。

<title>要素をすこし変更してみます。

<title>@ViewData["Title"] - RpMovie</title>

他にも、RazorPagesMovieと記述している個所があるので、RpMovieに変更します。

タグヘルパー

_Layout.cshtmlには、以下のような<a>タグがあります。

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

この <a>タグは ASP.NET Coreで利用できるタグヘルパーです。

実際のHTMLの<a>要素に似ていますが、次のような属性値が書いてあります。

asp-area="" asp-page="/Index"`

この2つの属性の値から、href属性が作成されます。

ここでは、以下のようにリンク先を変更します。

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

これで、ページ上部の RpMovieリンクをクリックすると、/Movies/indexページに遷移するようになります。

ここで実行してみます。以下が、ブラウザの開発者ツールで確認したHTMLの該当箇所です。

<a class="navbar-brand" href="/Movies">RpMovie</a>

クリックすると、/Movies/Index.cshtml のページに遷移します。

@RenderBody()

_Layout.cshtmlには、以下の行が存在します。

@RenderBody()

この行が、当該ページ(Index.chtml)に置き換わります。
つまり、全てのページで共通する部分を _Layout.cshtml に書いておき、ページごとに異なる部分を それぞれのページの cshtmlに記述することで、レイアウトを統一できるというわけです。

Pages/_ViewStart.cshtml ファイル

ちなみに、この _Layout.cshtmlは置き換えが可能です。 Pages/_ViewStart.cshtml ファイルに、デフォルトのレイアウトページ名が設定されています。

@{
    Layout = "_Layout";
}

ここを変更すれば、別のレイアウトページが利用されるようになります。(まあ、ここを変更することはないと思いますが...)

次回は、残りのページ(主にCreate.cshtml/ Create.cshtml.cs) について見ていきます。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?