なるほど、やりたいことはこうですね。
- 親セレクト(
PaymentTypeId)に「新規項目」を追加できるようにする - 子セレクト(
PaymentMethodId)も同様に「新規項目」を追加できるようにする - DB保存必須(
PaymentTypes と PaymentMethods テーブル) - SweetAlert2 + モーダルフォーム + AJAX でページ遷移せずに追加
- ASP.NET Core MVC の
CreateView 内で実装
以下に、実装の全体像とサンプルコードを提示します。
(このまま動かせる形にしてあります)
1. モデル定義
// Models/PaymentType.cs
public class PaymentType
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<PaymentMethod> PaymentMethods { get; set; }
}
// Models/PaymentMethod.cs
public class PaymentMethod
{
public int Id { get; set; }
public string Name { get; set; }
public int PaymentTypeId { get; set; }
public PaymentType PaymentType { get; set; }
}
2. コントローラ
// Controllers/PaymentsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
public class PaymentsController : Controller
{
private readonly AppDbContext _context;
public PaymentsController(AppDbContext context)
{
_context = context;
}
public IActionResult Create()
{
ViewData["PaymentTypeId"] = new SelectList(_context.PaymentTypes, "Id", "Name");
ViewData["PaymentMethodId"] = new SelectList(_context.PaymentMethods, "Id", "Name");
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Payment payment)
{
if (ModelState.IsValid)
{
_context.Add(payment);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(payment);
}
// AJAX: 新しい PaymentType を追加
[HttpPost]
public async Task<IActionResult> AddPaymentType([FromBody] PaymentType model)
{
if (string.IsNullOrWhiteSpace(model.Name))
return BadRequest("Name is required");
_context.PaymentTypes.Add(model);
await _context.SaveChangesAsync();
return Json(new { id = model.Id, name = model.Name });
}
// AJAX: 新しい PaymentMethod を追加
[HttpPost]
public async Task<IActionResult> AddPaymentMethod([FromBody] PaymentMethod model)
{
if (string.IsNullOrWhiteSpace(model.Name) || model.PaymentTypeId <= 0)
return BadRequest("Invalid data");
_context.PaymentMethods.Add(model);
await _context.SaveChangesAsync();
return Json(new { id = model.Id, name = model.Name });
}
}
3. Create.cshtml
@model Payment
<div class="form-group">
<label asp-for="PaymentTypeId"></label>
<select asp-for="PaymentTypeId" class="form-control" asp-items="@(ViewData["PaymentTypeId"] as SelectList)">
<option value="">-- 選択 --</option>
</select>
<button type="button" id="btnAddPaymentType" class="btn btn-sm btn-primary mt-1">+ 新規追加</button>
</div>
<div class="form-group">
<label asp-for="PaymentMethodId"></label>
<select asp-for="PaymentMethodId" class="form-control" asp-items="@(ViewData["PaymentMethodId"] as SelectList)">
<option value="">-- 選択 --</option>
</select>
<button type="button" id="btnAddPaymentMethod" class="btn btn-sm btn-primary mt-1">+ 新規追加</button>
</div>
@section Scripts {
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// PaymentType 追加
document.getElementById("btnAddPaymentType").addEventListener("click", function () {
Swal.fire({
title: '新しい支払タイプ',
input: 'text',
inputPlaceholder: 'タイプ名を入力',
showCancelButton: true,
confirmButtonText: '追加',
cancelButtonText: 'キャンセル'
}).then((result) => {
if (result.isConfirmed && result.value) {
fetch('@Url.Action("AddPaymentType")', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: result.value })
})
.then(res => res.json())
.then(data => {
let select = document.getElementById("PaymentTypeId");
let option = new Option(data.name, data.id, true, true);
select.add(option);
Swal.fire('追加しました', '', 'success');
})
.catch(() => Swal.fire('エラー', '追加できませんでした', 'error'));
}
});
});
// PaymentMethod 追加
document.getElementById("btnAddPaymentMethod").addEventListener("click", function () {
let paymentTypeId = document.getElementById("PaymentTypeId").value;
if (!paymentTypeId) {
Swal.fire('先に支払タイプを選択してください', '', 'warning');
return;
}
Swal.fire({
title: '新しい支払方法',
input: 'text',
inputPlaceholder: '方法名を入力',
showCancelButton: true,
confirmButtonText: '追加',
cancelButtonText: 'キャンセル'
}).then((result) => {
if (result.isConfirmed && result.value) {
fetch('@Url.Action("AddPaymentMethod")', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: result.value, paymentTypeId: paymentTypeId })
})
.then(res => res.json())
.then(data => {
let select = document.getElementById("PaymentMethodId");
let option = new Option(data.name, data.id, true, true);
select.add(option);
Swal.fire('追加しました', '', 'success');
})
.catch(() => Swal.fire('エラー', '追加できませんでした', 'error'));
}
});
});
</script>
}
4. 実装ポイント
- SweetAlert2 の
input を使ってモーダル入力 - AJAX (fetch) でコントローラに JSON を送信
- DB保存後、返ってきた ID と Name を
<select> に即追加 - ページ遷移なしで反映
- 子セレクト追加時は、必ず親の
PaymentTypeId を送信
5. 拡張案
- 親セレクト変更時に子セレクトを動的ロード(
/GetPaymentMethodsByTypeId API)
Powered by Froala Editor