ASP.NET Core MVC 3.1 入門 その５ 「View」

# はじめに

ASP.NET Core MVC 3.1 の View について、自分が学んだことを備忘録として記載します。
このフレームワークに殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます。

### 前回の記事

[ASP.NET Core MVC 3.1 入門 その４ 「Routing」](https://qiita.com/yagi405/items/ca9d6f17ff87073ed8cd)

### 今回の流れ

* MVCパターンの説明
* Viewの説明
* Viewを作ってみる
* 動かしてみる
* 特殊なViewテンプレートの説明

### 今回のゴール

*  MVCパターンにおけるViewの役割を理解する
*  Viewを追加できる
*  特殊なViewテンプレートとその役割を知る

### 本記事に含まれない内容
以下は別の記事で紹介する予定です。

* `@model @using @inject`
* HTMLヘルパー/タグヘルパー
* 部分ビュー
* ビューエンジンとは

### 環境
<dl>
    <dt>IDE</dt>
    <dd>Visual Studio 2019</dd>

    <dt>言語</dt>
    <dd>C#</dd>
</dl>

# MVCパターン

コントローラーの説明に入る前に、
まずはMVCパターンについて確認しておきましょう。
全体像を把握したうえで、View について具体的にみていきます。

### MVCパターンとは

画面表示と、ビジネスロジックの分離を実現するためのアーキテクチャ（設計手法）です。

要はHTMLの生成といった画面表示のための処理と
DBアクセスのようなデータの管理/操作をするための処理
これらを分離させるために先人が考え出したプログラムの作り方の一つです。

MVCというのは構成要素の略称で、以下の単語の先頭文字をそれぞれ取り出したものです。

> Model – View – Controller

実現できると、それぞれが分離しているので変更・修正があった場合、お互いに影響を受けづらく、Testability (テスト容易性) が高まるというメリットがあります。

画面に表示する項目の書式変更によってDBアクセス処理に影響が出たり、
DBの軽微な定義変更によって画面表示処理に影響が出るといった事態が発生しづらくなります。

### 各要素の役割

| 要素       | 概要                                                                                       |
|------------|--------------------------------------------------------------------------------------------|
| Model      | データの管理/操作といったビジネスロジックを担当                                      |
| View       | データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当 |
| Controller | ModelとViewの橋渡しを担当                                                            |

# View

## Viewとは

MVCの全体像を把握したうえで、 **View** について具体的にみていきます。
MVCにおいて、 データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当する要素です。

>  画面（HTML)の生成

というのが主なお仕事です。
後述するViewテンプレートとControllerから渡されるデータを組み合わせて出力するHTMLを生成します。

## Viewの構成要素

### Viewエンジン

`View`メソッドの呼び出しによって起動し、
Viewテンプレートと、Controllerから渡されるデータを組み合わせて出力するHTMLを生成します。
ASP.NET Core MVC では「**Razor**」が既定のViewエンジンとして採用されています。

### Viewテンプレート

拡張子が「.cshtml」のファイルです。

**配置場所と命名**

`/Views/コントローラー名/アクションメソッド名.cshtml`

とすることをおすすめします。
アクションメソッドで呼び出される`View`メソッドが
明示的に指定しない限り、`/Views/コントローラー名/アクションメソッド名.cshtml`を読み込むためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/f8c76ae2-5178-ffab-75ce-89fbf4f3b06e.png)

一部の特殊なテンプレートについては、`Views`または`Views/Shared`に配置します。特殊なテンプレートについては、後述します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/a1894a98-cc56-f790-be47-cb69f525105f.png)

### Viewスクリプト

Viewテンプレート内に記述するスクリプトです。
Viewエンジン固有の言語（Razor）で記述していきます。

#  Viewを追加する

では実際にViewを作ってみましょう。

### ASP.NET Core MVC 3.1 のプロジェクトを作成する

まずは「Web アプリケーション（モデル ビュー コントローラー）」テンプレートでプロジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/53953c98-61bd-a5a4-58ca-07a48d538379.png)

### ソリューションエクスプローラーを眺める

各フォルダ/ファイルの概要については[こちらの記事](https://qiita.com/yagi405/items/4ab1c88d9ea3339b98ac)をご参照ください。
前述したとおり、Viewテンプレートは`/Views/コントローラー名/アクションメソッド名.cshtml`に配置するようにしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/c18a903a-1e16-66de-32cf-fc013baded07.png)

###  **Controller** の追加

`HelloController`を追加します。
`Controller`については[こちらの記事](https://qiita.com/yagi405/items/5591d1f581db7978d3e8)をご参照ください。

```cs
public class HelloController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}
```

###  **View** の追加

新しい **View** を追加してみます。

> アクションメソッド（Indexメソッド）のメソッド名を右クリック > ビューの追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/5d3c8c47-3684-4be3-006c-b4ba119a9e5c.png)

と進んでください。

### スキャフォールディング

以下のようなウィンドウが立ち上がると思います。
「スキャフォールディング」というのは、要は**雛形を自動生成する機能**のことを示します。
今回はViewの雛形を自動生成してもらうために、この機能を使います。

自動で`/Views/コントローラー名/`に配置してくれたり、指定したモデルクラスの新規作成(`Create`)や一覧表示(`List`)のテンプレート（Bootstrap向けのcssセレクタまで付与されたもの）を用意してくれたりと、手間が省けるので、 Viewを追加する際には、この手順でスキャフォールディング機能を利用することをおすすめします。

今回は「Razor ビュー」> 「Empty（モデルなし）」を選択します。
上述した通り、よりリッチなテンプレートもありますが、まずはシンプルなViewを使っていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/de68c5ac-3a8a-1fda-3b97-9fd58c48a785.png)

続いてビュー名ですが、`アクションメソッド名`とします。
アクションメソッドで呼び出される`View`メソッドが
明示的に指定しない限り、`/Views/コントローラー名/アクションメソッド名.cshtml`を読み込むためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/6529483a-f3ba-504e-4e8c-46921baae8dc.png)

無事に`Hello`コントローラーの`Index`アクションメソッドと対応する **View(Index.cshtml)** を追加することができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/2628cf8c-0951-18d4-757d-f5fc6aff3373.png)

#  Viewのコードを確認する

以上でViewは追加できたと思います。
では、追加されたViewを見ていきましょう。

### Index.cshtml

まず注目して頂きたいのは`<h1>`タグです。
Viewスクリプトには、お馴染みの HTML/CSS/Javascript をゴリゴリ書いていくことができます。
HTML/CSS/Javascript を用いてページの構成を作りつつ、C#のコードを利用して動的な値を埋め込んでいくというのがView開発の流れになります。

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

<h1>Index</h1>
```

次に`@`や`ViewData`といった見慣れない構文が目に付くかと思います。これらの構文については後述します。

説明の便宜上、`HelloController`と`Index.cshml`に少々手を加えます。

まずは`HelloController`の`Index`メソッドを以下のように修正します。

```cs
public IActionResult Index()
{
    ViewBag.Message = "Hello World!"; //追加
    ViewData["Now"] = DateTime.Now; //追加
    return View();
}
```

続いて`Index.cshtml`も以下のように修正します。

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

<h1>Index</h1>

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>
```

# 動作確認

ここまで来たら、実行してみましょう。
URLを書き換えるのが手間なので、`Startup.cs`の`Configure`メソッドを以下のように修正し、`HelloController`の`Index`アクションメソッドが呼び出されるように、Routingの設定をしておきます。
Routingについては[こちらの記事](https://qiita.com/yagi405/items/ca9d6f17ff87073ed8cd)をご参照ください。

```cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //省略

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Hello}/{action=Index}/{id?}"); //Home => Hello
    });
}
```

`Index`アクションメソッドで設定した値が画面に表示されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/952977ab-173c-883a-9e33-3e833bb4bd41.png)

# View変数（ViewBag/ViewData）

```html
<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>
```

`ViewBag`と`ViewData`をView変数と呼びます。
要はViewスクリプトに埋め込む値のことです。
**Controller** のアクションメソッドから **View** にデータを渡すことができます。

> Controller側で表示に必要なデータを用意しておき、Viewではデータを埋め込む場所や表示方法を定義する

というのが **Controller** と **View** の基本的な関係となります。

### ViewBag

Controllerでプロパティとして値を設定しておくと、 **View**で参照することができます。
**View**内で値を設定、参照することも可能です。

型を指定することはできず、Intelisenceも効きません。

```cs
public IActionResult Index()
{
    ViewBag.Message = "Hello World!";
    //省略
}
```

```html
<h2>@ViewBag.Message</h2>
```

### ViewData

`Dictionary`としてキーと値を設定することができます。
用途は`ViewBag`と同様です。やはり型指定もできません。

```cs
public IActionResult Index()
{
    ViewData["Now"] = DateTime.Now; //追加
    //省略
}
```

```html
<p>@ViewData["Now"]</p>
```

### ViewBag vs ViewData

お好みで、どちらを採用しても良いと思います。
`ViewBag`を使うと、`ViewData`の`Dictionary`ベースの読みづらいコードを記述する必要はなくなりますが、DLRによって解釈されるコードを使いたくないのであれば`ViewData`をおすすめします。

### 多用は禁物

View変数の利用は、 **Controller** から **View** にデータを渡す最も単純な方法です。

ただし、多用は禁物です。
型の指定ができないので、例えば **Controller** と **View** を別の開発者が担当していた場合、 **View** 側の開発者からすれば、一体どんなデータが設定されてくるのか、見当がつかないかもしれません。

そんな時は、後々記事としてあげますが、強く型指定された`ViewModel`を利用すべきと筆者は考えます。

## @（コードナゲット）

続いて、`@`について説明していきます。
Viewスクリプトにおいて、**C#のコード**であることを示すキーワードです。

一行の場合、明示的に閉じる必要はありません。（セミコロンは不要です）コード行がどこで閉じるか解析してくれます。

```html
<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>
```

コードブロックを記述したいときは`@{...}`とします。

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

# 特殊なViewテンプレート

特定のアクションメソッドと対応しない特殊なViewが存在します。
ここでは最も基本的な2つを紹介します。

## レイアウトページ（_Layout.cshtml）

### まずは動作確認

再度実行してみましょう。
以下のような画面が表示されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/952977ab-173c-883a-9e33-3e833bb4bd41.png)

少々違和感があります。
`Index.cshtml`に記述したコードはこれだけです。
にも関わらず、それっぽいヘッダーやフッターが付いていますね。

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

<h1>Index</h1>

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>
```

これは**レイアウトページ（`_Layout.cshtml`）**という仕組みによるものです。
複数のページで共通するデザインの外枠を定義するための仕組みです。
レイアウトページを指定しておくと、ヘッダー、フッター、サイドメニューといった共通のデザインを一か所にまとめることができます。

変更する際も、レイアウトページだけを変更すれば一気に適用されるので、保守性も高まります。

レイアウトページは`Views/Shared/`に格納されています。
レイアウトページだけではなく、複数のViewテンプレートから利用し得るものは基本ここに配置します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/4948a65a-013a-87a1-22b0-72edb3a90886.png)

`_Layout.cshtml`のコードを確認しましょう。
`header`と`footer`の間に以下のコードが見つかると思います。

```html
@*省略*@
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>
@*省略*@
```

重要なのは`@RenderBody`という部分です。
ここに個別のViewスクリプト（例えばさきほど作成した`Index.cshtml`）が埋め込まれます。

これが、特に記述した記憶のないヘッダーやフッターが付いていた理由です。`_Layout.cshtml`に記述されていたというわけです。

### _Layout.cshtml

レイアウトページの名称は必ずしも`_Layout.cshtml`である必要はありません。また、`Views/Shared/`に格納しなければならないという規約もありません。
`_Layout.cshtml`とはただのViewテンプレートの1つに過ぎないのです。

では、なぜ`_Layout.cshtml`がレイアウトページとして検出されているのでしょうか？理由は後述します。

### 複数のレイアウトページ

これは雑記等、別の記事で紹介しますが、レイアウトページは複数用意することもできます。
例えば、メインのレイアウトページ、管理者用のレイアウトページ、未ログインユーザー向けのレイアウトページなどです。

## _ViewStart.cshtml

`ViewStart.cshtml`とは、Viewの処理に当たって、**最初に処理される特別なファイル**です。

重要なのはレイアウトページと異なり、**名前空間もファイル名も固定**です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/5812d234-cbc7-e005-067f-3626a18baa4d.png)


### 役割

一般的にはViewで使用するレイアウトページを指定するコードを記述します。実際にテンプレートでも`_Layout`をレイアウトページとして扱う旨のコードが記述されていることが確認できます。

ただし、個別のViewでレイアウトページを改めて指定した場合はそちらが優先されます。

```html
@{
    Layout = "_Layout";
}
```

これが、ただのViewテンプレートの1つに過ぎない`_Layout.cshtml`がレイアウトページとして検出されている理由です。`_ViewStart.cshtml`で指定されていたというわけです。

他にも、View関連のスタートアップコードが必要な場合はここに記述します。

### 規約

**ファイル名**

`_ViewStart.cshtml`

**配置場所（名前空間）**

`Views/` 直下

以上です。
ありがとうございました。