Visual Studio Express 2012 for Web でいってみる 25.Partial View Ajax.BeginFormでレコード編集
尚、初心者が行き当たりばったりにいろいろ動かしてみたときのメモなので、記載内容には勝手な解釈や誤りもあるかと思います。お気づきの点がありましたら、ご指摘よろしくお願いします。
実は、まだ試作中の状態で課題の全てが解決している訳ではないのですが、今の状態をメモしておかないと忘れてしまいそうなのでメモを残します。
プログラミングMicrosoft ASP.NET MVC ASP.NET MVC 3対応版 (マイクロソフト公式解説書)
- 作者: Dino Esposito,日本マイクロソフト井上章監訳,株式会社クイープ
- 出版社/メーカー: 日経BP社
- 発売日: 2012/05/08
- メディア: 単行本
- クリック: 34回
- この商品を含むブログ (4件) を見る
尚、使用したデータベースは「NorthwindJ」で『サンプルで学ぶ ASP.NET MVC アプリケーション開発』http://msdn.microsoft.com/ja-jp/data/ff723829.aspx
ナビから「テスト」をクリックすると上記のページが表示されます。
このページのリストボックスで「商品コード」を指定し、「編集Form」もしくは「編集Link」をクリックすると、
データアノテーション機能の働きでエラーメッセージが表示されます。
それでは、ソースを貼り付けしておきます。edmxについては今回は割愛します。
[model]
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Mvc4ApplicationQ.Models { //VMShouhinSitei:商品コード指定用フォームのmodel public class VMShouhinSitei { private NorthwindJEntities db = new NorthwindJEntities(); [Required] [DisplayName("商品CD")] public int 商品コード { get; set; } //View Modelにフォーム上で使用しない項目を置かないように。 //フォーム上に表示されていなくても検証は行われます。 // 商品のDropDownList用の商品リスト public IEnumerable<SelectListItem> Get商品List() { // --- 選択可能な商品リストの生成 --- var 商品SelectItemList = new List<SelectListItem>(); foreach (var c in from c in this.db.商品 orderby c.商品コード select c) { var listItem = new SelectListItem { Value = c.商品コード.ToString(), Text = c.商品名 }; 商品SelectItemList.Add(listItem); } return 商品SelectItemList; } } //VMShouhin:商品マスタの編集用model public class VMShouhin { [Required] [DisplayName("商品CD")] public int 商品コード { get; set; } [Required] [MaxLength(20)] [DisplayName("商品名")] public string 商品名 { get; set; } } }
先頭の商品コードを指定するページ用のmodel [ VMShouhinSitei ]と商品マスタの編集を行うページ用のmodel [ VMShouhin ]の2つのクラスを用意しました。
[Base.cshtml] 先頭の商品コードを指定するページ
@model Mvc4ApplicationQ.Models.VMShouhinSitei @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryval") <script type="text/javascript"> $(document).ready(function () { $("#btnajaxForm").click(function () { $("#pvShouhin").show(); }) $("#ajaxlink").click(function () { $(this).attr("href", "/Shouhin/_editLink?ShouhinCD=" + $("#ShoouhinCD").val()); $("#pvShouhin").show(); }) }); </script> @using (Ajax.BeginForm("_editform", "Shouhin", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "pvShouhin", LoadingElementId = "loading1" })) { @*↓Ajax.BeginFormに続く{}内にある項目がコントローラに正しく受け渡される。*@ @Html.DisplayNameFor(model => model.商品コード) @Html.DropDownListFor(model => model.商品コード, new SelectList(Model.Get商品List(),"Value","Text"), new{ id="ShoouhinCD" }) <br/><hr /> <input ID ="btnajaxForm" type="submit" name="btnEdit" value="編集Form" /> <img id="loading1" src="@Url.Content("~/Content/images/ajax-loader.gif")" alt="" class="loader" style = "display:none;"/> } <hr /> @Ajax.ActionLink("編集Link","_editLink","Shouhin", new { ShouhinCD = 0}, new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "pvShouhin", LoadingElementId = "loading2"}, new {ID ="ajaxlink", style = "height:30px; width:60px; float:left; border-style: solid;border-color:#787878; border-width:1px;" } ) <img id="loading2" src="@Url.Content("~/Content/images/ajax-loader.gif")" alt="" class="loader" style = "display:none;"/> <br/><br/><hr /> @* PartialView の挿入場所*@ <div id="pvShouhin" style = "border-style: solid; border-color:#B8B8B8; border-width:1px; padding:4px;display:none"></div>
まず、
@Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryval")
AjaxFormを使う為この2行が必要ですが、適当に扱わないと痛い目にあいます。
私の場合、この商品コード指定ページに記述済みにも関わらず、商品編集ページにも聞き込んでしまってました。商品編集ページでは特にエラーメッセージが出るわけでもなくしばらく気が付かなかったのですが、「登録」ボタンをクリックすると2回登録処理が実行されてました。
javascript 部分には、「編集Form」ボタンクリック時と「編集Link」クリック時にPartialView部分を見えるようにしています(更新後に隠しているので)。また「編集Link」クリック時にはリンク先を商品コードをパラメータにもつように変更しています。
このページでは、PartialViewを2つの方法で表示可能になっています。
「編集Form」ボタンクリック:@using Ajax.BeginForm
「編集Link」クリック:@Ajax.ActionLink
今回は、商品コード指定がリストボックスとなっています。省略されることがないので検証不要ですが、入力して内容の検証も行うのであれば、Ajax.BeginFormを使う方がいいと思います。
[_edit.cshtml] 商品マスタレコード編集用PartialView
@model Mvc4ApplicationQ.Models.VMShouhin <script type="text/javascript"> $(document).ready(function () { //ボタンをdisabledするとコントローラのUpdateアクションにはnullが渡される $("#MFMLbtnCreate").click(function () { $("#MFMLbtnCreate").hide(); $("#MFMLbtnUpdate").hide(); }) $("#MFMLbtnCreate").click(function () { $("#MFMLbtnCreate").hide(); $("#MFMLbtnUpdate").hide(); }) }); function onEditCompletedWithSuccessUnobtrusive(data, status, xhr) { var ServerMSG = xhr.getResponseHeader('Content-ServerMSG'); //alert(ServerMSG); if (ServerMSG == "Success") { $('#pvShouhin').hide(); } else { $("#MFMLbtnCreate").show(); $("#MFMLbtnUpdate").show(); } } </script> @using (Ajax.BeginForm("Update", "Shouhin", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "pvShouhin", OnSuccess = "onEditCompletedWithSuccessUnobtrusive" } )) { @Html.ValidationSummary(true) <fieldset> <legend>詳細</legend> <div class="editor-label"> @Html.LabelFor(model => model.商品コード) </div> <div class="editor-field"> @Html.EditorFor(model => model.商品コード) @Html.ValidationMessageFor(model => model.商品コード) </div> <div class="editor-label"> @Html.LabelFor(model => model.商品名) </div> <div class="editor-field"> @Html.EditorFor(model => model.商品名) @Html.ValidationMessageFor(model => model.商品名) </div> @* 更新ボタン *@ <button id="MFMLbtnCreate" type="submit" name="Button" value="Create">登録</button> <button id="MFMLbtnUpdate" type="submit" name="Button" value="Update">変更保存</button> </fieldset> }
スクリプトの部分では [ 登録 ] や [ 変更保存 ]ボタンがクリックされたとき、その2つのボタンを.hide() しています。更新の2重実行の防止の為です。disabledを使うのが一般的でしょうか? 実はこのタイミングでsubmitボタンをdisabledしてしまうと、コントローラーの引数(Button)にはnullが渡されてしまいます。代替え策としての hide です。
onEditCompletedWithSuccessUnobtrusive()はAjax.BeginForm のオプション OnSuccessで指定しているファンクションです。Ajaxリクエストが成功したときに呼び出されるファンクションです。サーバーサイドでセットしたメッセージをResponse Headerから取り出し、更新成功か否かを判定しています。
new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "pvShouhin",
OnSuccess = "onEditCompletedWithSuccessUnobtrusive"
}
))
{.....}
Ajax.BeginFormの引数 "Update", "Shouhin",は [ 登録 ] や [ 変更保存 ]ボタンがクリックされたときのAjax呼び出し指定で、Shouhinコントローラーの"Update"アクションが呼び出され制御はそちらに移ります。Updateアクションでサーバー側の処理が終わる際PartialViewを戻され、"pvShouhin"に再度表示されるはずです。ちょっと言葉尻が弱いのは、ここうまくいってないからです。
VMShouhin model = new VMShouhin();
return PartialView("_edit", model);
てな感じで、空のPartialViewを戻していて、デバッグでステップ実行すると、確かにmodelは空で渡されているですが、表示されるのは [ 登録 ] ボタンクリック時点の内容になります。「更新完了です」とかのメッセージをmodelにセットして表示しようとしていたのですが、今の私の力ではできませんでした。アノテーションとの絡みかもしれませんが不明です。
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Reflection; using System.Web; using System.Web.Mvc; using Mvc4ApplicationQ.Models; namespace Mvc4ApplicationQ.Controllers { public class ShouhinController : Controller { private NorthwindJEntities db = new NorthwindJEntities(); // // GET: /Shouhin/ public ActionResult Base() { // ビューモデルを生成 var model = new VMShouhinSitei(); return View(model); } //@Ajax.ActionLinkのPOST [AjaxOnly] public ActionResult _editLink(int ShouhinCD) { VMShouhin model = new VMShouhin(); if (Request.IsAjaxRequest()) { 商品 shouhin = (from s in this.db.商品 where s.商品コード == ShouhinCD select s).FirstOrDefault(); model.商品コード = shouhin.商品コード; model.商品名 = shouhin.商品名; } return PartialView("_edit", model); } //@using (Ajax.BeginFormのPOST [AjaxOnly] public ActionResult _editform(VMShouhinSitei indata) { VMShouhin model = new VMShouhin(); if (Request.IsAjaxRequest()) { 商品 shouhin = (from s in this.db.商品 where s.商品コード == indata.商品コード select s).FirstOrDefault(); model.商品コード = shouhin.商品コード; model.商品名 = shouhin.商品名; } return PartialView("_edit", model); } //Attribute[AjaxOnly]汎用ルーチンなので本番ではコントローラーの外のクラスにします public class AjaxOnlyAttribute : ActionMethodSelectorAttribute { public override bool IsValidForRequest( ControllerContext controllerContext, MethodInfo methodInfo) { return controllerContext.HttpContext.Request.IsAjaxRequest(); } } //_editから呼び出される更新処理 public ActionResult Update(VMShouhin inputdata, string Button) //↑:引数にはPartialViewのmodelを指定しないとデータアノテーションが効かなくなる。 //↓:if (ModelState.IsValid)を必ず記述すること。書いとかないとエラーのまま更新してしまう。 { string ServerMSG = ""; if (ModelState.IsValid) { //更新処理...中身は省略.... switch (Button) { case "Create": ServerMSG = "Success"; break; case "Update": ServerMSG = "Success"; break; default: break; } HttpContext.Response.AddHeader("Content-ServerMSG", ServerMSG); VMShouhin model = new VMShouhin(); return PartialView("_edit", model); } //エラーメッセージの追加 ModelState.AddModelError("", "更新処理は実行されませんでした。"); ServerMSG = "Update process not carried out"; HttpContext.Response.AddHeader("Content-ServerMSG", ServerMSG); return PartialView("_edit"); } }
return PartialView("_edit", model);