唐突ですが、カテゴリーの配下にサブカテゴリーがある場合、入力時なんかはカテゴリーが選択された後はサブカテゴリーはそのカテゴリーの配下のものに絞られた方が便利ですよね。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のプラグインでドロップダウンに見せることもできるでしょう(適当)。
実行結果
以上、久しぶりの更新なのにかなり駆け足でしたが、今日はここまで。…正直、ここまで試行錯誤しすぎて疲れました。