情報応用演習Ⅰ(2024)

【T7b】複数のモデルクラスの連携(7/9)

プロジェクトタイプ(注意: 本文参照)
プロジェクト名T7b
ソリューション名PIT7
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

7b-7. 「学科」のための Createアクション の変更

このウェブアプリはチュートリアル【T5b】で作成したものを改造したものなので, いまのところT5bと同じ画面遷移を持っている.このアプリの画面遷移を_に再掲する.

作成するアプリの画面イメージと画面遷移

データモデルを変更したので,その新規作成や編集,表示のためのビューも変更しなければならない. まずは_①の新規作成のためのアクション,つまりStudentsコントローラーの Createアクションを修正することにしよう.今回は_に示す通り,所属学科を選択するための ドロップダウンリストを追加する.

Createビューの変更点

ドロップダウンリストに関してはチュートリアル【T5b】でも列挙型の値を選択させるのに使用した. 今回の場合のように「学科」の情報を選択させるドロップダウンリストを作成するためには,当然のことながらデータベースから「学科」の 一覧を取り出してビューに渡す必要がある.このための処理は,StudentsコントローラーのCreateアクション(GET用)で行う.

ドロップダウンリスト( select 要素)の選択肢を設定するためには SelectListItemクラスの オブジェクトのコレクションを作って, select 要素に asp-items タグヘルパーを使用してこのコレクションを指定すればよい. 以前の列挙型の例では,HTMLヘルパーのGetEnumSelectList()メソッドが この指定した列挙型の型情報からSelectListItemのコレクションを作成してくれていたが,今回はこのコレクションを手動で作成する必要がある.

では Controllers/StudentsController.cs のCreateアクション(GET用)を_に示す通りに書き換えよう. ソースコードの冒頭に_に示すusingディレクティブを追記しなければならない点に注意しよう.

SelectListItemクラスを使用するために必要なusingディレクティブ
1
using Microsoft.AspNetCore.Mvc.Rendering;
StudentsControllerクラスの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Createアクション(GET用)
public IActionResult Create() 
{
    // LINQ を使って Departments から SelectListItem のコレクションを作る
    ViewBag.DepartmentList = (from d in _context.Departments
                              orderby d.Id ascending
                              select new SelectListItem() { Value = d.Id.ToString(), Text = d.Name }).ToList();

    return View(new Student());
}

ここでは2つの技術を使用している. ViewBagプロパティLINQをつかったコレクションの変換 である. 前者に関してはここで初めて紹介する.後者は2年次のプログラミング演習Ⅲ(2023)第10回で詳しく説明しているため,ここでは軽く触れる程度にとどめる.

5行目で使用しているViewBagプロパティは, 以前に説明したViewDataプロパティと同等のもので,アクションからビューに何らかのデータを渡すために使用することができる汎用コンテナである. 今回は作成したSelectListItemのコレクションをビューに渡すためにViewBagプロパティを使用している1ViewDataと違うのはこのプロパティの型がダイナミックオブジェクトと呼ばれるものである点である. ダイナミックオブジェクトについての詳しい説明はリンク先に譲るが,このタイプのオブジェクトに対するプロパティの読み書きやメソッド呼び出しといった操作は実行時に解決されるという特徴がある. もう少し噛み砕いて言えば,このタイプのオブジェクトには 適当なプロパティ名でどんなデータでも格納させることができる_の5行目でViewBag.DepartmentListというプロパティに対する代入を行っているが,これは ViewData["DepartmentList"]に代入するのと同等である.このためViewData["キー"]に対して代入したものを, ViewBag.キーとして取り出すことも,逆にViewBag.キーに対して代入したものを ViewData["キー"]として取り出すことも可能である.

また,7行目では「LINQをつかったコレクションの変換」(参考:プログラミング演習Ⅲ(2023;金澤クラス)第10回)のテクニックを利用している. ここではLINQの select 句でSelectListItemクラスのコンストラクタを呼び出すことによって,Departmentクラスのコレクションを SelectListItemクラスのコレクションに変換している.これまでの例では select 句には範囲変数そのものを渡す使い方しかしていなかったが,このようにして簡便にコレクションの生成を行うことも可能である.

ここで「所属学科」の入力のためのドロップダウンリスト用のデータ生成は,Createアクション(GET用)以外にも 何カ所かで必要になるので,ヘルパーメソッドとして切り出してそれを呼び出すように変更しよう._に 示すように変更する.

StudentsControllerクラスの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// .ViewBag に「所属学科」のリストを入れるためのヘルパーメソッド
private void SetupDepartmentList()
 => ViewBag.DepartmentList = (from d in _context.Departments
                              orderby d.Id ascending
                              select new SelectListItem() { Value = d.Id.ToString(), Text = d.Name }).ToList();

// Createアクション(GET用)
public IActionResult Create() 
{
    SetupDepartmentList(); // ↑を呼び出す

    return View(new Student());
}

つぎにこのCreateアクションのためのビューも変更しよう.Views/Students/Create.cshtmlを_に示すように変更しよう.

Views/Students/Create.cshtmlの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@{
    ViewData["Title"] = "新規学生の登録";
 
    if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
}
 
@model Student
 
<form asp-action="Create">
    @* 姓(LastName) のための入力欄 *@
    <label asp-for="@Model.LastName"></label>:
    <input asp-for="@Model.LastName" />
    <span asp-validation-for="@Model.LastName"></span>
    <br />
 
    @* 名(FirstName) のための入力欄 *@
    <label asp-for="@Model.FirstName"></label>:
    <input asp-for="@Model.FirstName" />
    <span asp-validation-for="@Model.FirstName"></span>
    <br />
 
    @* 性別(Sex) のための入力欄 *@
    <label asp-for="@Model.Sex"></label>:
    <select asp-for="@Model.Sex" asp-items="Html.GetEnumSelectList<SexType>()" ></select>
    <span asp-validation-for="@Model.Sex"></span>
    <br />

    @* 所属学科(DepartmentId) のための入力欄 *@
    <label asp-for="@Model.DepartmentId"></label>
    <select asp-for="@Model.DepartmentId" asp-items="@ViewBag.DepartmentList"></select>
    <span asp-validation-for="@Model.DepartmentId"></span>
    <br />
 
    @* 電話番号(PhoneNumber) のための入力欄 *@
    <label asp-for="@Model.PhoneNumber"></label>:
    <input asp-for="@Model.PhoneNumber" />
    <span asp-validation-for="@Model.PhoneNumber"></span>
    <br />
 
    @* メールアドレス(Mail) のための入力欄 *@
    <label asp-for="@Model.Mail"></label>:
    <input asp-for="@Model.Mail"/>
    <span asp-validation-for="@Model.Mail"></span>
    <br />
 
    @* 誕生日(Birthday) のための入力欄 *@
    <label asp-for="@Model.Birthday"></label>:
    <input asp-for="@Model.Birthday" />
    <span asp-validation-for="@Model.Birthday"></span>
    <br />

    <input type="submit" value="送信" />
</form>
 
<a asp-action="Index">一覧に戻る</a>
 
@section Scripts
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

ドロップダウンリストがHTMLの select 要素に対応することは以前にも説明した.30行目の asp-items タグヘルパーに 指定している値を除けば,その直前の「性別」(Sex)の入力欄のための記述と同じである.「性別」の入力欄と異なり, 「所属学科」の入力欄では asp-items タグヘルパーには,Createアクション(GET用)においてViewBagプロパティに渡した SelectListItemのリスト(.DepartmentList)を指定している.

これによりフォームから入力されるデータが増えたので,この送信を処理するためコントローラー側のアクション, つまりCreateアクション(POST用)も修正しておこう. Controllers/StudentsController.cs のCreate()メソッド([HttpPost]属性を指定しているほう)を _に示すように変更しよう.

StudentsControllerクラスの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Createアクション(POST用)
[HttpPost]
[ValidateAntiForgeryToken] //                                                                  ↓追加
public IActionResult Create([Bind("FirstName, LastName, Sex, PhoneNumber, Mail, Birthday, DepartmentId")] Student student)
{
    if (ModelState.IsValid)
    {
        student.Registered = DateTime.Now;

        _context.Add(student);
        _context.SaveChanges();

        return RedirectToAction(nameof(Details), new { id = student.Id });
    }
    else
    {
        SetupDepartmentList(); // 入力に誤りがある場合は,再びフォームを表示させる必要がある.
                               // このため「所属学科」のデータを .ViewBag に入れておく必要があるので,
                               // SetupDepartmentList() メソッドを呼び出しておく.

        return View(student);
    }
}

ここでの変更は以下の二か所である.

  • 4行目
    • 引数の[Bind]属性の指定にDepartmentIdを追加していること.
  • 17行目
    • 入力値に誤りがある場合(17~19行目)はCreateのビューに戻さないといけないため,GET用アクションと同様に学科のリストをViewDagにセットするために_SetupDepartmentList()メソッドを呼び出している.

以上で_①のCreateアクションのビューの変更は完了である.


  1. 今回は分かりやすさのためにこの方法を用いているが, この方法は推奨されていない 点に注意が必要である.ビューに関連付けられるモデルは1つしかないので,このためにそのページで値を入力させるためのモデルクラスとSelectListItemのコレクションを一つに組み込んだしたクラス定義して用いる方が無難ではある(参考: Tag Helpers in forms in ASP.NET Core - Microsoft Docs). ↩︎

Last updated on 2024-06-10
Published on 2024-06-10

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.