唐突ですが、カテゴリーの配下にサブカテゴリーがある場合、入力時なんかはカテゴリーが選択された後はサブカテゴリーはそのカテゴリーの配下のものに絞られた方が便利ですよね。Winフォームとかだったらカテゴリー決定後にイベント発生させてってできるわけなんですがWebになるとどうすりゃいいいのって話になるわけです。
というわけでとりあえずモデルを提示しときます。
カテゴリーモデル
public class Category { public int Id { get; set; } public string Name { get; set; } public virtual List<SubCategory> SubCategories { get; set; } }
サブカテゴリーモデル
public class SubCategory { public int Id { get; set; } public int CategoryId { get; set; } public string Name { get; set; } public virtual Category Category { get; set; } public virtual List<Article> Articles { get; set; } }
商品モデル
public class Article { public int Id { get; set; } public string Name { get; set; } public int SubCategoryId { get; set; } public virtual SubCategory SubCategory { get; set; } }
イメージとしては商品入力時、カテゴリードロップダウンリストを決定したら、サブカテゴリードロップダウンリストの部分のみを更新って感じでしょうか。1部分のみの更新といえば部分ビューの出番ですね。
さっさと結果から言っちゃうとできたことはできたんですが、カテゴリーをAJAXヘルパーを使うため商品モデルの拡張とビュー側でドロップダウンリストが使えないという問題が…。以下、サンプルコードです。
商品モデル拡張
public class ArticleEdit { public Article Article { get; set; } public List<Category> Categories { get; set; } }
コントローラー(一部抜粋)
public ActionResult Create() { var model = new ArticleEdit { Categories = db.Categories.ToList() }; ViewBag.SubCategoryId = new SelectList(db.SubCategories, "Id", "Name"); return View(model); } [HttpPost] public ActionResult Create(ArticleEdit model) { if (ModelState.IsValid) { db.Articles.Add(model.Article); db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.SubCategoryId = new SelectList(db.SubCategories, "Id", "Name", model.Article.SubCategoryId); return View(model); } //これが部分ビューのアクション public ActionResult SubCategoryDropDownList(int categoryId = 1) { ViewBag.SubCategoryId = new SelectList(db.SubCategories.Where(p => p.CategoryId == categoryId), "Id", "Name"); return View(); }
Createビュー(フォーム部分のみ)
@using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Article</legend> <div class="editor-label"> @Html.LabelFor(model => model.Article.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Article.Name) @Html.ValidationMessageFor(model => model.Article.Name) </div> <div class="editor-label"> カテゴリー </div> <div class="editor-field"> <ul> @foreach (var category in Model.Categories) { <li> @Ajax.ActionLink(category.Name, "SubCategoryDropDownList", new { categoryId = category.Id}, new AjaxOptions{UpdateTargetId="subcategoryarea"}) </li> } </ul> </div> <div class="editor-label"> サブカテゴリー </div> <div id="subcategoryarea" class="editor-field"> @Html.Partial("SubCategoryDropDownList") @Html.ValidationMessageFor(model=>model.Article.SubCategoryId) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> }
SubCategoryDropDownListビュー
<div class="editor-field"> @Html.DropDownList("Article.SubCategoryId", ViewBag.SubCategoryId as SelectList) </div>
何とも分かりにくいサンプルになってしまいましたが、ポイントはCreateビューの「@AJAX.ActionLink()」メソッドと「@Html.Partial()」メソッドです。初回呼出時にはPartialメソッドによって部分ビュー(SubCategoryDropDownList)が呼び出され、カテゴリーのリンククリック時にはActionLinkメソッドで部分ビューが更新されます。ActionLinkメソッドでは動的なパラメータは使えませんと怒られたのでカテゴリーをわざわざViewBagではなくてモデルに配置しています。カテゴリーはとりあえず<li>要素で表してますが、jQueryのプラグインでドロップダウンに見せることもできるでしょう(適当)。
実行結果
以上、久しぶりの更新なのにかなり駆け足でしたが、今日はここまで。…正直、ここまで試行錯誤しすぎて疲れました。