「AjaxHelper を利用した作成・更新フォーム」で、AjaxHelper を利用して非同期通信で作成・更新の部分フォームを取得し、現在表示している画面に挿入することができました。ここでは、サーバー側で把握した検証エラーをデータ入力しているときと同じ状態の画面で通知できるようにしてみます。
方法としては、「入力ダイアログで入力された値のサーバーでの検証エラーの表示」と同じく、「データ検証でエラーが発生した場合、エラーメッセージと入力データをビューへ渡し、クライアント側では受け取った HTML ドキュメントからエラー発生を把握して、入力されていたデータをフォームにセットしてからダイアログを表示させる」ようにします。
サーバー側で検証エラーが発生すると、こんな感じで通知されます。
モデルに変更はありません。
コントローラーの前に、ビューから始めます。まずは ListContacts.cshtml から。
@model IDictionary<int, string>
@{
ViewBag.Title = "ListContacts";
}
@section scripts {
<script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
}
<h2>@ViewBag.Title</h2>
@Ajax.ActionLink("追加", "CreateContact", new AjaxOptions() { UpdateTargetId = "editForm" })
<table>
<tr>
<th>名前</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Value)
</td>
<td>
@Ajax.ActionLink("詳細", "GetContact", new { id = @item.Key },
new AjaxOptions() { UpdateTargetId = "editForm" }) |
@Ajax.ActionLink("編集", "EditContact", new { id = @item.Key },
new AjaxOptions() { UpdateTargetId = "editForm" })
</td>
</tr>
}
</table>
<div>
@Html.ActionLink("戻る", "Index")
</div>
<div style="color:Red;margin-top:1em">@Html.Encode(TempData["errMsg"])</div>
<div id="editForm" style="margin-top:1em">
</div>
@if (TempData["model"] != null)
{
<div id="errForm">
@Html.Action("EditContact")
</div>
var errModel = (TestAjaxPartialForm001.Models.DataModel)TempData["model"];
<script type="text/javascript">
<!--
function setForm(actionName, id, name, email, phone) {
var fieldTitle = "編集画面";
var buttonText = "変更を保存";
var linkText = "編集画面を閉じる";
if (actionName == "CreateContact") {
fieldTitle = "追加画面";
buttonText = "追加して保存";
linkText = "追加画面を閉じる";
}
$("#errForm form")
.attr("action", "/Home/" + actionName + "/" + id)
.find("legend").text(fieldTitle)
.end()
.find("input[id=Id]").val(id)
.end()
.find("input[id=Name]").val(name)
.end()
.find("input[id=Email]").val(email)
.end()
.find("input[id=Phone]").val(phone)
.end()
.find("input[type=submit]").val(buttonText);
$("#errForm a").text(linkText);
}
$(function () {
var errData = $("#errData");
setForm(errData.find("div[class=actionName]").text(), errData.find("div[class=id]").text(),
errData.find("div[class=name]").text(), errData.find("div[class=email]").text(),
errData.find("div[class=phone]").text());
});
//-->
</script>
<div id="errData" style="display:none;">
<div class="actionName">@Html.Encode(TempData["actionName"])</div>
<div class="id">@Html.Encode(errModel.Id)</div>
<div class="name">@Html.Encode(errModel.Name)</div>
<div class="email">@Html.Encode(errModel.Email)</div>
<div class="phone">@Html.Encode(errModel.Phone)</div>
</div>
}
TempData[“errMsg”] でエラーメッセージを受け取り、TempData[“model”] で検証エラーになった入力データを受け取っています。
<div id=”editForm”> の上にエラーメッセージを表示する場所を設けています。
検証エラーが発生すると、TempData[“model”] に入力したデータが設定されるので、当該データがあれば、それを <div id=”errData”> のところにセットし、その後 Javascript でセットしたデータをフォームにセットしています。なお、セットするフォームは、
<div id=”errForm”>
@Html.Action(“EditContact”)
</div>
で、更新用のフォームをセットしています(更新用のフォームには Id (キー項目)の保存用エリアがあるため)。
setForm(actionName, id, name, email, phone) メソッドの呼び出しは、jQuery の ready イベントで行うようにしています(省略表記で記述)。
部分ビューに変更はないので、最後にコントローラー(HomeController.cs)です。
using System;
== 中略 ==
[HttpPost]
public ActionResult CreateContact(DataModel model)
{
if (ModelState.IsValid)
{
try
{
TestModel.CreateContact(model);
}
catch (ValidationException e)
{
TempData["actionName"] = "CreateContact";
TempData["errMsg"] = e.Message;
TempData["model"] = model;
return RedirectToAction("ListContacts");
}
return RedirectToAction("ListContacts");
}
return View();
}
public ActionResult EditContact(int? id)
{
if (Request.Url.ToString().IndexOf("EditContact") >= 0) // クライアントから呼ばれたか?
{
if (Request.IsAjaxRequest() && id.HasValue) // 非同期通信 && idは数字 か?
{
var contact = TestModel.GetContact(id.Value);
return PartialView(contact);
}
return new EmptyResult();
}
return PartialView();
}
[HttpPost]
public ActionResult EditContact(DataModel model)
{
if (ModelState.IsValid)
{
try
{
TestModel.ModifyContact(model);
}
catch (ValidationException e)
{
TempData["actionName"] = "EditContact";
TempData["errMsg"] = e.Message;
TempData["model"] = model;
return RedirectToAction("ListContacts");
}
return RedirectToAction("ListContacts");
}
return View();
}
メールアドレスの重複チェックでエラーになると ValidationException が投げられるので、それを受けて TempData にエラー情報をセットしています。
また、エラーがある場合には ListContacts ビューからエラー表示用のフォームが要求(引数なし)されてくるので、EditContact(int? id) と引数を null 許容型にしておき、呼び出し元の確認に「if (Request.Url.ToString().IndexOf(“EditContact”) >= 0)」を行っています(ListContacts ビューの @Html.Action(“EditContact”) で呼び出された場合には、Request.Url には ListContacts が入っているため)。
こんな感じで、サーバー側で把握した検証エラーをデータ入力しているときと同じ状態の画面で通知することができるようになりました。
なお、あくまで感触を掴むために書いたものなので、エラー・ハンドリングを省いています。もし参考にされる方がいらしたら、ちゃんとエラー処理をしてください 😉
何か気がついたこととかあったら、指摘していただけると嬉しいです 🙂
次回は、データ入力をダイアログ・ウィンドウでできるようにします。
「AjaxHelper を利用した作成・更新フォーム(サーバー側検証エラーの表示)」への3件のフィードバック