jQuery ajax を使って、部分ビューを応答として返すアクションメソッドにフォームデータを POST 送信し、返ってきた部分ビューの html をページ内の指定の場所にレンダリングする方法を書きます。
先の記事「ASP.NET Core MVC と Ajax ライブラリ」では、Microsoft の Ajax ライブラリ jquery.unobtrusive-ajax.js を利用して、Ajax で部分ビューを呼び出してページ内の指定の場所に表示する方法を書きました。
それと機能的に同じことを、Microsoft の Ajax ライブラリの助けを借りないで、jQuery ajax を利用して実装する方法です。
CSRF 用の隠しフィールドのデータを含めてフォームデータを全て jQuery ajax を使って POST 送信し、部分ビュー用のアクションメソッドに付与した [ValidateAntiForgeryToken] 属性での検証ができるようにしていることに注目してください。
問題はフォームデータをクライアント側でどのように取得するかですが、jQuery の .serialize() メソッドを使うと form データを application/x-www-form-urlencoded 形式の文字列として取得できます。それを jQuery ajax の data パラメータに設定して POST 送信してやればアクションメソッドで受けることができます。
Controller と View のサンプルコードを以下に書いておきます。コードは ASP.NET Core 3.1 MVC のものです。.NET Framework MVC5 の場合はライブラリ jquery.unobtrusive-ajax.js と AjaxHelper を使う方が簡単でよさそうです。
使っているのは Microsoft が提供する Northwind サンプルデータベースの Customers テーブルです。その CompanyName をドロップダウンリストに表示し、ユーザーが選択してボタンをクリックすると部分ビューを呼び出して、選択した Customers のレコードの詳細を指定した場所(下の View のコードで言うと <div id="result"></div> の中)に書き出すものです。
Controller / Action Method
先の記事「ASP.NET Core MVC と Ajax ライブラリ」のものと同じです。
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace MvcCoreApp.Controllers
{
public class AjaxController : Controller
{
private readonly NorthwindContext _context;
public AjaxController(NorthwindContext context)
{
_context = context;
}
// jQuery ajax で下のアクションメソッド Details
// を呼んで詳細 Customer データの部分ビューを表示。
// CSRF 用の隠しフィールドのデータも送信して検証で
// きるようにする。
public IActionResult Index2()
{
// 全部取得すると長すぎるので Take(10) した
var list = _context.Customers.Take(10);
ViewData["customers"] =
new SelectList(list, "CustomerId", "CompanyName");
return View();
}
// 部分ビュー用のアクションメソッド
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Details(string customerid)
{
if (string.IsNullOrEmpty(customerid))
{
return NotFound();
}
var customer = _context.Customers.Find(customerid);
if (customer == null)
{
return NotFound();
}
return PartialView(customer);
}
}
}
View (Index2.cshtml)
ボタンには input type="submit" タイプ(クリックすると form を submit する)と input type="button" タイプ(クリックしても何も起こらない)の 2 つを配置して、両方期待通り動くことを確認してみました。
@{
ViewData["Title"] = "Index2";
}
<h1>Index2</h1>
<form id="form1" asp-action="Details"
asp-controller="Ajax" method="post">
<select id="customerid" name="customerid"
asp-items="@ViewBag.customers">
</select>
<input type="submit" value='詳細表示 (type="submit")' />
<input id="button1" type="button"
value='詳細表示 (type="button")' />
</form>
<div id="result"></div>
@section Scripts {
<script type="text/javascript">
//<![CDATA[
// [詳細表示 (type="submit")] ボタンをクリック
// したときの処理
// ボタンクリックで form が submit されるという
// 動きになるので、form 要素の submit イベントに
// リスナをアタッチして jQuert ajax で処理する
$("#form1").on("submit", function (event) {
// submit されては困るのでキャンセル
event.preventDefault();
// jQuery ajax を使って、部分ビューを応答と
// して返すアクションメソッド Ajax/Deteils
// にフォームデータを POST 送信する
$.ajax({
type: "POST",
url: "/Ajax/Details",
// フォームデータを取得
data: $(this).serialize(),
// コールバック function の引数 data に
// 部分ビューの html ソースが渡される
success: function (data) {
$("#result").empty();
$("#result").append(data);
},
error: function (jqXHR, status, error) {
$('#result').text('Status: ' + status +
', Error: ' + error);
}
});
})
//]]>
</script>
<script type="text/javascript">
//<![CDATA[
// [詳細表示 (type="button")] ボタンをクリック
// したときの処理
// ボタン要素の click イベントにリスナをアタッチ
// して jQuert ajax で処理する
$("#button1").on("click", function () {
// input type="button" タイプのボタンはクリ
// ックしても from が submit されることは
// ないので event.preventDefault(); は不要
// jQuery ajax を使って、部分ビューを応答と
// して返すアクションメソッド Ajax/Deteils
// にフォームデータを POST 送信する
$.ajax({
type: "POST",
url: "/Ajax/Details",
// フォームデータを取得
data: $("#form1").serialize(),
// コールバック function の引数 data に
// 部分ビューの html ソースが渡される
success: function (data) {
$("#result").empty();
$("#result").append(data);
},
error: function (jqXHR, status, error) {
$('#result').text('Status: ' + status +
', Error: ' + error);
}
});
})
//]]>
</script>
}
部分ビューのコードは、先の記事「ASP.NET Core MVC と Ajax ライブラリ」のものとほとんど同じなので省略します。
Microsoft の Ajax ライブラリ jquery.unobtrusive-ajax.js は ASP.NET Core MVC ではサポートされてないのかもしれません。なので、上記のように jQuery ajax を使うのが正解のような気がします。