【ASP.NET Core MVC】ASP.NET Core MVCであみだくじを作ってみる 3 (モデルの追加)

# 概要
前回：[ASP.NET Core MVCであみだくじを作ってみる 2 (ビュー,コントローラーの追加)](https://qiita.com/plusM/items/fc23683bd81a0a568f00)
今回は、モデルを追加してビューと紐付けます。
モデルのデータを表示、編集し、別コントローラーへ移るところも作ります。

# モデルを追加する
ソリューションエクスプローラーの「Models」を選択します。
右クリックでメニューを表示し、追加⇒クラスを選択します。
名前を「KujiModel.cs」にして追加します。

あみだくじには「タイトル」「くじ数」「結果(あたり、はずれ等)」があります。
それぞれ、[データアノテーション](https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.dataannotations?view=netframework-4.8)の属性を付けて入力時に検証を行えるようにしておきます。
選択したくじ番号も作っておきます。コードは以下の通りです。

```cs:KujiModel.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Amidakuji.Models
{
    /// <summary>
    /// くじモデル
    /// </summary>
    public class KujiModel
    {
        // タイトル
        [StringLength(20, ErrorMessage = "タイトルは20文字以内で入力してください")]
        public string Title { get; set; }
        // くじの数
        [Range(1, 9, ErrorMessage = "1～9の数字を入力してください")]
        public int NumberOfKuji { get; set; }
        // くじの結果
        public List<ResultModel> Result { get; set; }
        // 選択したくじ番号
        public int SelectId { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public KujiModel() { }
    }

    /// <summary>
    /// くじの結果モデル
    /// </summary>
    public class ResultModel
    {
        // 内容
        [StringLength(5, ErrorMessage = "くじの結果は5文字以内で入力してください")]
        public string Item { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ResultModel() { }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="item"></param>
        public ResultModel(string item) 
        {
            Item = item;
        }
    }
}
```

# ビューでモデルを利用できるようにする
Homeビューで上記モデルのデータを入力しようと思います。
Homeコントローラーでモデルのインスタンスを生成し、ビューに渡すように変更します。

```cs:HomeController.cs
using Amidakuji.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Diagnostics;

namespace Amidakuji.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

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

        public IActionResult Index()
        {
            // モデルのインスタンスを生成し、ビューに渡す
            var model = new KujiModel();
            model.Title = "晩御飯決定あみだくじ";
            model.NumberOfKuji = 5;
            model.Result = new List<ResultModel>() 
            { 
                new ResultModel("おでん"),
                new ResultModel("湯豆腐"),
                new ResultModel("シチュー"),
                new ResultModel("ぶり大根"),
                new ResultModel("ラーメン")
            };
            return View(model);
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}
```

ビューにデータを表示させてみます。
Home\Index.cshtmlを以下のように変更します。
ここでは、[グリッドレイアウト](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Grid_Layout)を使ってみました。

```html:Home\Index.cshtml
@model Amidakuji.Models.KujiModel

@{
    ViewData["Title"] = "あみだくじ";
}

<style>
    /*あみだくじデータの設定領域*/
    .kuji-items {
        display: grid; /*グリッドレイアウトにする*/
        grid-auto-columns: 100px; /*1列あたりの幅 100px*/
        grid-auto-rows: 36px; /*1行あたりの高さ 30px*/
        background-color: #99CCCC; /*背景色*/
        margin: 0px auto; /*中央寄せ*/
        padding:10px;
    }

    /*あみだくじデータの設定領域の入力項目*/
    .kuji-items > input {
        height: 30px; /*高さ*/
    }

    /*くじを作るボタン*/
    .create_btn {
        font-weight: bold;
        padding: 0.25em 0.5em;
        text-decoration: none;
        color: #FFF;
        background: #008080;
        margin-left: 10px;
        height: 30px; /*高さ*/
    }
</style>

<form method="post" asp-controller="Home" asp-action="Index">
    <div class="kuji-items">
        <!--タイトル入力欄-->
        <label style="grid-row:1;grid-column:1">タイトル : </label>
        <input type="text" asp-for="Title" value="@Model.Title" style="grid-row:1;grid-column:2 / span 3" />

        <!--くじの数入力欄-->
        <label style="grid-row:2;grid-column:1">くじの数 : </label>
        <input type="text" asp-for="NumberOfKuji" value="@Model.NumberOfKuji" style="grid-row:2;grid-column:2" />

        <!--結果入力欄を増減するボタン-->
        <button class="create_btn" style="grid-row:2;grid-column:5 / span 2" asp-route-name="set">結果を設定する</button>

        <!--結果入力欄-->
        <label style="grid-row:3;grid-column:1">結果 : </label>
        @for (var i = 0; i < @Model.Result.Count; i++)
        {
            <input type="text" asp-for="Result[i].Item" value="@Model.Result[i].Item" style="grid-row:@(i + 3);grid-column:2 / span 3" />
        }

        <!--あみだくじ作成ボタン-->
        <button class="create_btn" style="grid-row:@(Model.Result.Count + 2);grid-column:5 / span 2" asp-route-name="create">くじを作る</button>

        <!--入力エラー表示欄-->
        <div asp-validation-summary="All" class="text-danger" style="grid-row:@(Model.Result.Count + 3);grid-column:2 / span 5"></div>
    </div>
</form>

```
1行目の「＠model Amidakuji.Models.KujiModel」で、
このビューはKujiModelを使いますよ、という宣言になります。
KujiModelのプロパティに「＠Model.ｘｘｘ」でアクセスできるようになります。
詳しくは、[ASP.NET Core の Razor 構文リファレンス](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/razor?view=aspnetcore-3.0)に書かれています。

途中で、＠forと書かれた行があります。
このように、Razorでは@記号によってC#のコードを埋め込むことができます。

ボタンを押した時にPOSTしたいので、formタグを使います。
formタグは、Webサーバーに情報を送信するための送信ボタンなどを含める区間です。
htmlタグについては[こちら](https://developer.mozilla.org/ja/docs/Web/HTML/Reference)

formタグのasp-controller、asp-actionは、ASP.NETのタグヘルパーです。
タグヘルパーについては[こちら](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-3.0#what-are-tag-helpers)
上記コードの場合、POSTでHomeControllerのIndexアクションを呼び出しますよ、という意味になります。
asp-controllerは、xxxControllerのxxxだけを指定します。

inputタグのasp-forは、[入力タグヘルパー](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.0#the-input-tag-helper)です。input要素をモデルにバインドします。

入力エラー表示欄のasp-validation-summaryは、[検証タグヘルパー](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.0#the-validation-tag-helpers)です。
モデルのプロパティに付けたデータアノテーション属性で入力値の検証を行い、エラーがあればメッセージを表示します。

# POSTした時のアクションを追加する
上記のHomeコントローラーでは、POSTした時もIndex()が呼ばれます。
Index()はGET用として、新しくPOSTした時のIndexアクションを追加します。
HomeコントローラーのIndexを以下のように変更します。

```cs:HomeController.cs
        [HttpGet]
        public IActionResult Index()
        {
            // 新しいくじモデルをビューに渡す
            var model = new KujiModel();

            model.Title = "晩御飯決定あみだくじ";
            model.NumberOfKuji = 5;
            model.Result = new List<ResultModel>() 
            { 
                new ResultModel("おでん"),
                new ResultModel("湯豆腐"),
                new ResultModel("シチュー"),
                new ResultModel("ぶり大根"),
                new ResultModel("ラーメン")
            };

            return View(model);
        }

        /// <summary>
        /// HomeビューでPOSTした時のアクション
        /// </summary>
        /// <param name="name"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult Index(string name, KujiModel model)
        {
            // 入力エラーがある時は何もしない
            if (!ModelState.IsValid)
                return View(model);

            // [結果を設定する]ボタンを押下した場合
            if (name == "set")
            {
                // くじの数が減った場合は、結果入力欄を減らす
                while (model.NumberOfKuji < model.Result.Count)
                {
                    model.Result.RemoveAt(model.Result.Count - 1);
                }

                // くじの数が増えた場合は、結果入力欄を増やす
                while (model.NumberOfKuji > model.Result.Count)
                {
                    model.Result.Add(new ResultModel());
                }

                return View(model);
            }

            // くじコントローラーを呼ぶ
            return RedirectToAction("Index", "Kuji");
        }
```
元々のIndex()には[HttpGet]を付け、GET用としました。
[HttpPost]を付けたIndexがPOSTで呼ばれるアクションになります。

パラメータの「string name」ですが、Home\Index.cshtmlのボタンにあるタグヘルパー「asp-route-name」で設定した"set"や"create"が渡されます。
パラメータ名とasp-route-xxxのxxx部分は同じである必要があります。xxx部分は自分で決めた文字列が使えます。
詳しくは[こちら](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.0#the-form-tag-helper) 
また、パラメータの「KujiModel model」には、ビューで入力したデータを反映したモデルが渡されます。

最後に戻り値をRedirectToActionとしたのは、別コントローラー(くじコントローラー)のアクションに移りたいためです。
ViewとRedirectToActionの違いは、[こちらの質問](https://codeday.me/jp/qa/20190121/184355.html)の回答を参考にしました。(自動翻訳なので少し変)


# 実行してみる
Homeを表示したところです。
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/6b8e6fc9-1850-90d1-2785-8259dd4fc922.png)
モデルとビューの紐付けが出来ています。

くじの数に範囲外の数字を入力し、結果を設定するボタンを押下してみます。
![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/9a813214-0a70-7c8b-3afa-effdaf8adeea.png)
データアノテーション検証が出来てます。

くじの数に範囲内の数字を入力し、結果を設定するボタンを押下してみます。
![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/dc0a1deb-9142-e256-51af-96d1c8b0f5dd.png)
[HttpPost]を付けたIndexアクション内の処理が行われました。

くじを作るボタンを押下してみます。
![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520699/ad8d9270-15be-bbf2-b019-ce09bdce8121.png)
Kujiビューに移りました。

# まとめ
今回は、モデルを追加してビューと紐付けるところまでを行いました。
次回は、cssを使ってビューのレイアウトを作成します。
次回分はこちら⇒[ASP.NET Core MVCであみだくじを作ってみる 4 (cssの作成)](https://qiita.com/plusM/items/f13d2644689531c1c6c1)