.NET Framework 版 MVC の話は先の記事「DropDownList への SelectList の渡し方」に書きましたが、その Core 版の話を聞きます。
Core 版 MVC アプリでは従前から提供されている Html ヘルパーの DropDownList, DropDownListFor メソッドに加えて、タグヘルパーというものが使えますのでそれを使った場合の例も書いておきます。
タグヘルパーに関する詳細は以下の Microsoft のドキュメントが参考になると思いますので、そちらを見てください(手抜きでスミマセン)。
SelectList のコンストラクタの第 4 引数に設定した selectedValue の通りに html にレンダリングされた時の option 要素に selected 属性を設定するにはどうするかという点がポイントでしたが、Html ヘルパーの DropDownList, DropDownListFor メソッドについてはそのあたりは .NET Framework 版と同様でした。
例えば ViewBag.CategoryID で SelectList を DropDownList メソッドに渡す場合は、第 1 引数は "CategoryID" という文字列、第 2 引数は null にします。詳しい説明は先の記事「DropDownList への SelectList の渡し方」を見てください。
select タグヘルパーを使う場合ですが、SelectList を ViewBag (または ViewData) 経由で渡す場合でかつ Model を使用しない場合は asp-for は使えませんので、select タグヘルパーに id, name 属性を設定して asp-items 属性に ViewBag (または ViewData) を渡します。
さらに、上に紹介した記事「ASP.NET Core のフォームのタグ ヘルパー」の「選択タグ ヘルパー」のセクションに Model で渡すサンプルコードが書いてあって、そのようにしても option 要素に selected 属性の設定が可能です。
その記事によると "We don't recommend using ViewBag or ViewData with the Select Tag Helper. A view model is more robust at providing MVC metadata and generally less problematic." とのことで、可能ならば SelectList を渡すには Model を使った方が良さそうです。(Model を使えないケースも多々ありそうですが)
以下に、(1) 1 項目だけ選択できるドロップダウンリスト、(2) 複数項目が選択可能なドロップダウンリスト、(3) 初期表示の際複数項目を指定して選択状態にするドロップダウンリストのサンプルコードを書いておきます。
(1) 1 項目だけ選択可
先の記事「DropDownList への SelectList の渡し方」と同じ条件です。
Model
select タグヘルパーに Model 経由で SelectList と初期状態の選択項目を渡すために使います。上に紹介した 3 番目の記事によると "asp-for 属性に指定されているプロパティが IEnumerable の場合、選択タグ ヘルパーは multiple = "multiple" 属性を自動的に生成します" とのことですので注意してください。
public class CategoryViewModel
{
public int Category { get; set; }
public SelectList Categories { get; set; }
}
Controller / Action Method
public async Task<IActionResult> Edit()
{
int selected = 3;
var categoryList = await _context.Categories.ToListAsync();
ViewBag.CategoryId = new SelectList(categoryList,
"CategoryId",
"CategoryName",
selected);
var model = new CategoryViewModel
{
Category = selected,
Categories = new SelectList(categoryList,
"CategoryId",
"CategoryName")
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int categoryid,
int categoryid2,
int category)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View();
}
View
@model MvcCore5App.Controllers.CategoryViewModel
@{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div class="form-group">
<label class="control-label" for="CategoryId">
@@Html.DropDownList 利用
</label>
@Html.DropDownList("CategoryId", null,
htmlAttributes: new { @class = "form-control" })
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + ViewBag 利用
</label>
<select id="categoryid2" name="categoryid2"
asp-items="@ViewBag.CategoryId"
class="form-control">
</select>
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + Model 利用
</label>
<select asp-for="Category"
asp-items="Model.Categories"
class="form-control">
</select>
</div>
<div class="form-group">
<input type="submit" value="Create"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
(2) 複数項目の選択可
Model
上記 (1) の Model と同じ。
Controller / Action Method
[HttpPost] 側のアクションメソッドの引数が int[] 型になっている点に注目してください。int[] 型にすることにより、ユーザーが複数項目を選択した結果を受け取れます。
public async Task<IActionResult> Edit2()
{
int selected = 3;
var categoryList = await _context.Categories.ToListAsync();
ViewBag.CategoryId = new SelectList(categoryList,
"CategoryId",
"CategoryName",
selected);
var model = new CategoryViewModel
{
Category = selected,
Categories = new SelectList(categoryList,
"CategoryId",
"CategoryName")
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit2(int[] categoryid,
int[] categoryid2,
int[] category)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View();
}
View
ユーザーが複数項目を選択できるよう multiple = "multiple" を付与しているところに注目してください。上にも書きましたが、asp-for 属性に指定されるプロパティが IEnumerable の場合、選択タグ ヘルパーは multiple = "multiple" 属性を自動的に生成しますとのことです。
@model MvcCore5App.Controllers.CategoryViewModel
@{
ViewData["Title"] = "Edit2";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit2</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Edit2">
<div class="form-group">
<label class="control-label" for="CategoryId">
@@Html.DropDownList 利用
</label>
@Html.DropDownList("CategoryId", null,
htmlAttributes: new
{
@class = "form-control",
multiple = "multiple"
})
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + ViewBag 利用
</label>
<select id="categoryid2" name="categoryid2"
asp-items="@ViewBag.CategoryId"
multiple="multiple"
class="form-control">
</select>
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + Model 利用
</label>
<select asp-for="Category"
asp-items="Model.Categories"
multiple="multiple"
class="form-control">
</select>
</div>
<div class="form-group">
<input type="submit" value="Create"
class="btn btn-primary" />
</div>
</form>
</div>
</div>
(3) 初期表示の際複数項目を選択状態にする
上の画像を表示したのがこれです。
Model
SelectList に代えて MultiSelectList を使っているところに注目してください。
public class MultiCategoryViewModel
{
public int[] Category { get; set; }
public MultiSelectList Categories { get; set; }
}
Controller / Action Method
初期表示の時点で複数項目が選択されるようにするため、int selected を int[] selected に代えて、それを MultiSelectList の第 4 引数に渡している点に注目してください。View に渡す Model も CategoryViewModel から MultiCategoryViewModel に変えています。
public async Task<IActionResult> Edit3()
{
int[] selected = new int[] { 1, 3, 5 };
var categoryList = await _context.Categories.ToListAsync();
ViewBag.CategoryId = new MultiSelectList(categoryList,
"CategoryId",
"CategoryName",
selected);
var model = new MultiCategoryViewModel
{
Category = selected,
Categories = new MultiSelectList(categoryList,
"CategoryId",
"CategoryName")
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit3(int[] categoryid,
int[] categoryid2,
int[] category)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View();
}
View
Model を CategoryViewModel から MultiCategoryViewModel に変えたこと、下のタグヘルパー <select> + Model 利用のコードには multiple = "multiple" を設定してないこと以外は上記 (2) の View と同じです。上の Model のコードの通り Category プロパティを int[] 型にしてそれを asp-for 属性に指定していますので、html には multiple = "multiple" 属性が自動的に生成されます。
@model MvcCore5App.Controllers.MultiCategoryViewModel
@{
ViewData["Title"] = "Edit3";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit3</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Edit3">
<div class="form-group">
<label class="control-label" for="CategoryId">
@@Html.DropDownList 利用
</label>
@Html.DropDownList("CategoryId", null,
htmlAttributes: new
{
@class = "form-control",
multiple = "multiple"
})
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + ViewBag 利用
</label>
<select id="categoryid2" name="categoryid2"
asp-items="@ViewBag.CategoryId"
multiple="multiple"
class="form-control">
</select>
</div>
<div class="form-group">
<label class="control-label" for="CategoryId2">
タグヘルパー <select> + Model 利用
</label>
<select asp-for="Category"
asp-items="Model.Categories"
class="form-control">
</select>
</div>
<div class="form-group">
<input type="submit" value="Create"
class="btn btn-primary" />
</div>
</form>
</div>
</div>