了解です。
では、前回の多階層ドロップダウン実装を、さらに実務向けに改良して再生成します。
今回は以下の点を追加します。
- 非同期ロード対応(Ajax):親カテゴリ選択時に子カテゴリを動的取得
- APIエンドポイント追加:JSONで階層データを返す
- Viewは初期表示を最小限に:必要な階層だけ読み込む
1. モデル(DBエンティティ)
// Models/Category.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int? ParentId { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; } = new List<Category>();
}
2. DbContext
// Data/ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Category> Categories { get; set; }
}
3. サービス(子カテゴリ取得)
// Services/CategoryService.cs
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class CategoryService
{
private readonly ApplicationDbContext _context;
public CategoryService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Category>> GetTopLevelCategoriesAsync()
{
return await _context.Categories
.Where(c => c.ParentId == null)
.OrderBy(c => c.Name)
.ToListAsync();
}
public async Task<List<Category>> GetChildrenAsync(int parentId)
{
return await _context.Categories
.Where(c => c.ParentId == parentId)
.OrderBy(c => c.Name)
.ToListAsync();
}
}
4. コントローラー(通常View + API)
// Controllers/MenuController.cs
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class MenuController : Controller
{
private readonly CategoryService _categoryService;
public MenuController(CategoryService categoryService)
{
_categoryService = categoryService;
}
// 初期表示(親カテゴリのみ)
public async Task<IActionResult> Index()
{
var parents = await _categoryService.GetTopLevelCategoriesAsync();
return View(parents);
}
// API: 子カテゴリ取得
[HttpGet]
public async Task<IActionResult> GetChildren(int parentId)
{
var children = await _categoryService.GetChildrenAsync(parentId);
return Json(children.Select(c => new { c.Id, c.Name }));
}
}
5. View(Ajax連動ドロップダウン)
@model List<Category>
<select id="parentCategory" class="form-select">
<option value="">-- 親カテゴリを選択 --</option>
@foreach (var parent in Model)
{
<option value="@parent.Id">@parent.Name</option>
}
</select>
<select id="childCategory" class="form-select mt-2" disabled>
<option value="">-- 子カテゴリを選択 --</option>
</select>
@section Scripts {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function(){
$('#parentCategory').on('change', function(){
var parentId = $(this).val();
var $child = $('#childCategory');
$child.empty().append('<option value="">-- 子カテゴリを選択 --</option>');
if(parentId){
$.getJSON('/Menu/GetChildren', { parentId: parentId }, function(data){
if(data.length > 0){
$.each(data, function(i, item){
$child.append($('<option>', { value: item.id, text: item.name }));
});
$child.prop('disabled', false);
} else {
$child.prop('disabled', true);
}
});
} else {
$child.prop('disabled', true);
}
});
});
</script>
}
6. Program.cs
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<CategoryService>();
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.MapDefaultControllerRoute();
app.Run();
改良ポイント
- 初期表示は親カテゴリのみ
- Ajaxで子カテゴリ取得
- 必要なときだけサーバーから取得し、パフォーマンス向上。
- APIエンドポイント分離
/Menu/GetChildrenでJSON返却、他のUIにも流用可能。
この構成なら、カテゴリ数が多くても高速に動作し、
UIも親カテゴリ選択後に必要な子カテゴリだけ表示できます。
もし希望なら、このAjax版を孫カテゴリまで連動する3階層ドロップダウンに拡張できます。
作りますか?
そうすれば「親 → 子 → 孫」まで自動連動します。
Powered by Froala Editor