2012/10/12

【ASP.Net】PartialViewをjQueryでお手軽モーダル表示…に失敗【MVC】

 なんだかあと一歩な感じなんでメモっとく。

 何がしたいかというとリストから新規作成を選択したらページ遷移せずにモーダルウィンドウで表示してみたいんです。で、色々考えてみた結果、PartialViewとjQueryのjqModalプラグインを使えばできるんじゃねーかという結論にいたったので実験したわけです。結果的に失敗しているので今回もちゃっちゃっとサンプルを載せます。

 jqModalプラグインについてはこちら→jqModal :: Minimalistic Modaling for jQuery
 
 モデル等については例によって使い回しです。

Userモデル
public class User:IValidatableObject
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    public int CategoryId { get; set; } 
        
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(10 < this.Name.Length)
        {
            yield return new ValidationResult("サーバーサイドで引っかかりました", new[] { "Name" });
        }
    }
}

 検証確認のため、適当な検証をくっつけてます。

Userコントローラー
public class UserController : Controller
{
    public ActionResult Index()
    {
        var categoris = new List<Category> { 
            new Category { Id = 1, Name = "A" }, 
            new Category { Id = 2, Name = "B" } 
        };
 
        var useres = new List<User> { 
            new User { Id = 1, CategoryId = 1, Name = "Tanaka" }, 
            new User { Id = 2, CategoryId = 2, Name = "Sato" } 
        };
 
        return View(useres);
    }
 
    public ActionResult Create()
    {
        return PartialView();
    }
 
    [HttpPost]
    public ActionResult Create(User target)
    {
        if (ModelState.IsValid)
        {
            //追加処理は省略
 
            return RedirectToAction("Index");
        }
 
        return PartialView(target);
    }
}

 これまたいい加減ですが、Create実行時に目的であるPartialViewで表示するように設定してます。

Indexビュー
@model IEnumerable<MvcTestProject01.Models.User>
 
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
@section styles{
    <link href="~/Content/jqModal.css" rel="stylesheet" type="text/css" />
}
 
@section scripts{
    <script src="~/Scripts/jqModal.js" type="text/javascript"></script>
    <script type="text/javascript">
 
        function createShow() {
            $('#jqmdialog').jqm({
                modal: true,
                ajax: '/User/Create'
            });
            $('#jqmdialog').jqmShow();
        }
 
    </script>
}
 
<h2>Index</h2>
 
<div id="jqmdialog" class="jqmWindow"></div>
 
<p>
    <a href="#" onclick="createShow()">Create New</a>
</p>
<table>
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.CategoryId)
        </th>
        <th></th>
    </tr>
 
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CategoryId)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}
 
</table>

 今回の肝であるjqModalプラグインを設定しています。流れとしては「リンククリックイベント(createShow())→ダイアログの定義→ダイアログの表示」って感じです。

Createビュー
@model MvcTestProject01.Models.User
 
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
 
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>User</legend>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.CategoryId)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.CategoryId)
            @Html.ValidationMessageFor(model => model.CategoryId)
        </div>
 
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
 
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

 こっちは何の変哲もないビューです。使ってみて初めて気がつきましたが、どうやら部分ビューは部分ビューでjquery等を定義してないと動かない模様です。最初これ飛ばしててクライアント検証が発生しなくてかなり困りました。

実行結果(Index表示時)

image

実行結果(Createクリック時)

image

 おぉー、出来た出来た!…と思いきや。

実行結果(サーバーサイド検証時)

image

 ちょっと分かりづらいんですがサーバーサイドで検証に引っかかってもう一度PartialView()が実行されるとモーダル表示が解けるどころかページ遷移が発生しちゃってるんですよ。アドレスも思いっきり「User/Create」って出ちゃってます。

 …そして今に至るわけです。ちょっと手の込んだことしないと解決できなそうな気がする。何とかできたら追記するかも?
pagetop