ASP.NET MVCはじめました~子画面の検索結果を親画面へ受け渡す
概要
子画面で検索を行い、その結果を親画面へ戻すという処理を実装してみます。
たとえば、トランザクションデータを検索する際の項目にマスタデータのIDが必要な場合、子画面でマスタデータを名称検索し、マスタデータのIDを親画面へ戻し、この値を使って検索するというようなケースを想定しています。
実装
jQueryの用意
MicrosoftのCDNからjQuery/jQuery UIとCSSを読み込みます。
■Mvc2App/Views/Shared/Site.Master
<head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title> <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="<%: Url.Content("~/Scripts/MicrosoftAjax.debug.js") %>"></script> <script type="text/javascript" src="<%: Url.Content("~/Scripts/MicrosoftMvcAjax.debug.js") %>"></script> <link href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.11.1/themes/smoothness/jquery-ui.css" rel="Stylesheet" /> <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script> <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.11.1/jquery-ui.min.js"></script> <asp:ContentPlaceHolder ID="ScriptContent" runat="server" /> </head> ...
コントローラーの用意
■Mvc2App/Controllers/GroupController.cs
namespace Mvc2App.Controllers { public class GroupController : Controller { private readonly IGroupService _service; public GroupController() : this(new GroupService()) { } public GroupController(IGroupService service) { _service = service; } ... //親画面での検索処理は省略 public ActionResult Search() { var model = new GroupSearchViewModel(); return View(model); } //子画面(ダイアログ)の部分ビュー生成 [ChildActionOnly] public ActionResult PersonIDSearch() { return PartialView(); } //子画面検索の制御 [HttpPost] public ActionResult SearchPeople(string personName) { var model = _service.PeopleSearch(personName); return PartialView("_peopleGrid", model); } } }
サービスインタフェースとサービスクラスの用意*1
■Mvc2App/Services/Abstractions/IGroupService.cs
namespace Mvc2App.Services.Abstractions { public interface IGroupService { PeopleSearchViewModel SearchPeople(string personName); } }
■Mvc2App/Services/GroupService.cs
SearchPeopleメソッドのPeopleRepositoryクラスについてはこちらの記事を見ていただければコードがあります。が!PeopleのスペルをPepleと誤記しているため罠となっています(;;´¬`;)*2
namespace Mvc2App.Services { public class GroupService : IGroupService { private readonly IGroupRepository _repository; public GroupService() : this(new GroupRepository()) { } public GroupService(IGroupRepository repository) { _repository = repository; } public PeopleSearchViewModel SearchPeople(string personName) { var model = new PeopleSearchViewModel(); var repos = new PeopleRepository(); var condition = new PeopleSearchConditionModel() { Name = personName }; model.data = repos.Search(condition); return model; } } }
ビューモデルの用意
■Mvc2App/ViewModels/Group/GroupSearchViewModel.cs
namespace Mvc2App.ViewModels.Group { public class GroupSearchViewModel : SearchResultViewModel<UserGroup> { public UserGroupSearchConditionModel conditions { get; set; } } public class UserGroupSearchConditionModel { public string ID { get; set; } public string GroupName { get; set; } public string PersonID { get; set; } public string Note { get; set; } public string UpdatedBy { get; set; } public string UpdateDate { get; set; } } }
ビュー(JavaScript)の用意
■Mvc2App/Views/Group/Search.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Mvc2App.ViewModels.Group.GroupSearchViewModel>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Search </asp:Content> <asp:Content ID="ScriptContent" ContentPlaceHolderID="ScriptContent" runat="server"> <script type="text/javascript" src="<%: Url.Content("~/Scripts/Libs/GroupSearch.js") %>"></script> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Search</h2> <% using (Html.BeginForm()) { %> <fieldset> <legend>Search Fields</legend> <div class="field-box"> <div class="field"> <%: Html.LabelFor(model => model.conditions.ID) %> <%: Html.EditorFor(model => model.conditions.ID)%> </div> <div class="field"> <%: Html.LabelFor(model => model.conditions.GroupName)%> <%: Html.EditorFor(model => model.conditions.GroupName)%> </div> <div class="field"> <%: Html.LabelFor(model => model.conditions.PersonID)%> <%: Html.EditorFor(model => model.conditions.PersonID)%> <a href="#" id="personIDSearch">ID検索</a> </div> <div class="field"> <%: Html.LabelFor(model => model.conditions.Note)%> <%: Html.EditorFor(model => model.conditions.Note)%> </div> <div class="field"> <%: Html.LabelFor(model => model.conditions.UpdatedBy)%> <%: Html.EditorFor(model => model.conditions.UpdatedBy)%> </div> <div class="field"> <%: Html.LabelFor(model => model.conditions.UpdateDate)%> <%: Html.EditorFor(model => model.conditions.UpdateDate)%> </div> </div> <div class="field-box"> <p> <input type="submit" value="Search" /> </p> </div> </fieldset> <%} %> <div id="personIDSearchDialog" style="display:none;"> <%: Html.Action("PersonIDSearch") %> </div> </asp:Content>
■Mvc2App/Scripts/Libs/GroupSearch.js
$(function () { $('#personIDSearch').click(function () { showDialog(); }); }); //dialog function showDialog() { $('#personIDSearchDialog').dialog({ title: "個人ID検索", width: 550, height: 400, modal: true, buttons: { "OK": function (event) { var result = ""; var record = $('#tPeople tr'); $.each(record, function () { if ($('td:nth-child(1) #personSelector:checked', this).val()) { var personIDValue = $('td:nth-child(2)', this).text(); result = result + personIDValue + ","; } }); $('#conditions_PersonID').val(result); $(this).dialog("close"); }, "キャンセル": function () { $(this).dialog("close"); } }, close: function () { $(this).dialog("destroy"); } }); }
ID検索リンクのクリックイベントでjQuery UIによるダイアログを生成し、中身はActionヘルパーで呼び出す部分ビューを表示します。
子画面検索のリクエストはAjax.BeginFormヘルパーを使用しさらに結果表示用の部分ビューを返します。
部分ビューの用意
■Mvc2App/Views/Group/PersonIDSearch.ascx
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %> <% using (Ajax.BeginForm("SearchPeople", new AjaxOptions() { UpdateTargetId = "peopleGrid" })) { %> <div> <label>名前</label> <input type="text" name="personName" /> <input type="submit" value="Search" /> </div> <%} %> <div id="peopleGrid"> </div>
■Mvc2App/Views/Group/_peopleGrid.ascx
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc2App.ViewModels.People.PeopleSearchViewModel>" %> <table id="tPeople"> <% if (Model.data != null) { %> <tr> <th>選択</th> <th>ID</th> <th>名前</th> <th>住所</th> <th>電話番号</th> </tr> <% foreach (var item in Model.data) {%> <tr> <td><%: Html.CheckBox("personSelector") %></td> <td><%: item.ID %></td> <td><%: item.Name %></td> <td><%: item.Address %></td> <td><%: item.PhoneNumber %></td> </tr> <%} %> <%} %> </table>
今回一番悩んだのが、チェックボックスで選択したデータを親画面へ戻す制御。
ダイアログで「OK」ボタンがクリックされた処理としてIDで指定したtableタグを元に子要素をぐるぐる回して判定してますが、もっとスマートな方法があるかも??
var result = ""; var record = $('#tPeople tr'); $.each(record, function () { if ($('td:nth-child(1) #personSelector:checked', this).val()) { var personIDValue = $('td:nth-child(2)', this).text(); result = result + personIDValue + ","; } }); $('#conditions_PersonID').val(result);
実行結果
親画面から、ID検索リンクをクリック。
検索用のダイアログが表示されるので、適当な名前で検索し、データを選択後「OK」ボタンをクリック。
選択したデータのIDが親画面へ表示されます。