なるほど、やりたいことはこうですね。
- ASP.NET Core MVC の CreateView に
- 親
<select>(PaymentTypeId) - 子
<select>(PaymentMethodId)
- 親に新しい項目を追加できる(SweetAlert2 で入力 → DB 保存 → 即反映)
- 子も同様に新しい項目を追加できる(SweetAlert2 で入力 → DB 保存 → 即反映)
- 親子は 協調表示(親選択で子が絞り込み)
以下に 完全動作するサンプル構成 を示します。
(Entity Framework Core を使用、SweetAlert2 は CDN で読み込み)
1. モデル定義
// Models/PaymentType.cs
public class PaymentType
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public ICollection<PaymentMethod> PaymentMethods { get; set; } = new List<PaymentMethod>();
}
// Models/PaymentMethod.cs
public class PaymentMethod
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int PaymentTypeId { get; set; }
public PaymentType PaymentType { get; set; } = null!;
}
2. DbContext
public class AppDbContext : DbContext
{
public DbSet<PaymentType> PaymentTypes { get; set; }
public DbSet<PaymentMethod> PaymentMethods { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}
3. コントローラー
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(Enumerable.Empty<SelectListItem>());
return View();
}
[HttpPost]
public IActionResult Create(int PaymentTypeId, int PaymentMethodId)
{
// 保存処理(例: 注文や支払いレコード)
return RedirectToAction(nameof(Create));
}
// 親追加
[HttpPost]
public IActionResult AddPaymentType(string name)
{
if (string.IsNullOrWhiteSpace(name))
return BadRequest("Name is required");
var type = new PaymentType { Name = name };
_context.PaymentTypes.Add(type);
_context.SaveChanges();
return Json(new { id = type.Id, name = type.Name });
}
// 子追加
[HttpPost]
public IActionResult AddPaymentMethod(int paymentTypeId, string name)
{
if (string.IsNullOrWhiteSpace(name))
return BadRequest("Name is required");
var method = new PaymentMethod { Name = name, PaymentTypeId = paymentTypeId };
_context.PaymentMethods.Add(method);
_context.SaveChanges();
return Json(new { id = method.Id, name = method.Name });
}
// 子リスト取得
[HttpGet]
public IActionResult GetPaymentMethods(int paymentTypeId)
{
var methods = _context.PaymentMethods
.Where(m => m.PaymentTypeId == paymentTypeId)
.Select(m => new { id = m.Id, name = m.Name })
.ToList();
return Json(methods);
}
}
4. Create.cshtml
@{
ViewData["Title"] = "Create Payment";
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<form asp-action="Create" method="post">
<div class="form-group">
<label>Payment Type</label>
<div class="input-group">
<select asp-for="PaymentTypeId" class="form-control" asp-items="@(ViewData["PaymentTypeId"] as SelectList)" id="PaymentTypeId"></select>
<button type="button" class="btn btn-outline-primary" id="btnAddType">+</button>
</div>
</div>
<div class="form-group mt-3">
<label>Payment Method</label>
<div class="input-group">
<select asp-for="PaymentMethodId" class="form-control" asp-items="@(ViewData["PaymentMethodId"] as SelectList)" id="PaymentMethodId"></select>
<button type="button" class="btn btn-outline-primary" id="btnAddMethod">+</button>
</div>
</div>
<button type="submit" class="btn btn-success mt-3">Save</button>
</form>
<script>
document.getElementById("PaymentTypeId").addEventListener("change", function () {
const typeId = this.value;
fetch(`/Payments/GetPaymentMethods?paymentTypeId=${typeId}`)
.then(res => res.json())
.then(data => {
const methodSelect = document.getElementById("PaymentMethodId");
methodSelect.innerHTML = "";
data.forEach(m => {
methodSelect.innerHTML += `<option value="${m.id}">${m.name}</option>`;
});
});
});
document.getElementById("btnAddType").addEventListener("click", function () {
Swal.fire({
title: '新しいPayment Type',
input: 'text',
inputPlaceholder: 'Type name',
showCancelButton: true,
confirmButtonText: '追加'
}).then(result => {
if (result.isConfirmed && result.value.trim() !== "") {
fetch('/Payments/AddPaymentType', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `name=${encodeURIComponent(result.value)}`
})
.then(res => res.json())
.then(data => {
const select = document.getElementById("PaymentTypeId");
const option = new Option(data.name, data.id, true, true);
select.add(option);
select.dispatchEvent(new Event('change')); // 子更新
});
}
});
});
document.getElementById("btnAddMethod").addEventListener("click", function () {
const typeId = document.getElementById("PaymentTypeId").value;
if (!typeId) {
Swal.fire('先にPayment Typeを選択してください');
return;
}
Swal.fire({
title: '新しいPayment Method',
input: 'text',
inputPlaceholder: 'Method name',
showCancelButton: true,
confirmButtonText: '追加'
}).then(result => {
if (result.isConfirmed && result.value.trim() !== "") {
fetch('/Payments/AddPaymentMethod', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `paymentTypeId=${typeId}&name=${encodeURIComponent(result.value)}`
})
.then(res => res.json())
.then(data => {
const
Powered by Froala Editor