以下は、前回のコードをさらに改良した ASP.NET Core MVC + jQuery + SweetAlert2 実装例です。
今回の変更点は以下の通りです。
- 重複チェック機能を追加(既存カテゴリ名と同じ場合は保存しない)
- SweetAlert2 の ローディング表示を追加(保存中や一覧再取得中にユーザー操作を防止)
- コード構造を整理し、関数化で可読性向上
<!-- CreateView の一部 -->
<select asp-for="IncomeCategoryId"
class="form-control"
id="IncomeCategoryId"
asp-items="@(ViewData["IncomeCategoryId"] as SelectList)">
<option value="add">+ 項目を追加</option>
</select>
<!-- SweetAlert2 CDN -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<!-- jQuery CDN -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function () {
const $select = $("#IncomeCategoryId");
$select.on("change", function () {
if ($(this).val() === "add") {
openAddCategoryDialog();
}
});
function openAddCategoryDialog() {
Swal.fire({
title: '新しい項目を追加',
text: '追加するカテゴリ名を入力してください',
input: 'text',
inputPlaceholder: '例: 家賃, 光熱費',
icon: 'question',
showCancelButton: true,
confirmButtonText: '保存',
cancelButtonText: 'キャンセル',
inputAttributes: { autofocus: 'autofocus' },
inputValidator: (value) => {
if (!value || !value.trim()) {
return 'カテゴリ名は必須です';
}
}
}).then((result) => {
if (result.isConfirmed) {
let newCategoryName = result.value.trim();
checkDuplicateAndSave(newCategoryName);
} else {
$select.val("");
}
});
}
function checkDuplicateAndSave(name) {
Swal.showLoading();
$.getJSON('/IncomeCategory/Exists', { name: name }, function (exists) {
if (exists) {
Swal.fire('重複エラー', '同じカテゴリ名が既に存在します', 'warning');
$select.val("");
} else {
saveCategory(name);
}
}).fail(function () {
Swal.fire('通信エラー', '重複チェックに失敗しました', 'error');
$select.val("");
});
}
function saveCategory(name) {
Swal.showLoading();
$.ajax({
url: '/IncomeCategory/CreateAjax',
type: 'POST',
data: { name: name },
success: function (response) {
if (response.success) {
refreshCategoryList(response.id);
Swal.fire('保存しました', '', 'success');
} else {
Swal.fire('エラー', response.message || '保存に失敗しました', 'error');
$select.val("");
}
},
error: function (xhr) {
Swal.fire('通信エラー', `ステータス: ${xhr.status}`, 'error');
$select.val("");
}
});
}
function refreshCategoryList(selectedId) {
Swal.showLoading();
$.getJSON('/IncomeCategory/GetAll', function (data) {
$select.empty();
$.each(data, function (i, item) {
$select.append($("<option>", {
value: item.id,
text: item.name
}));
});
$select.append($("<option>", { value: "add", text: "+ 項目を追加" }));
if (selectedId) {
$select.val(selectedId);
}
}).fail(function () {
Swal.fire('通信エラー', 'カテゴリ一覧の取得に失敗しました', 'error');
});
}
});
</script>
サーバー側(ASP.NET Core MVC)例
[HttpGet]
public IActionResult Exists(string name)
{
bool exists = _context.IncomeCategories
.Any(c => c.Name.Trim() == name.Trim());
return Json(exists);
}
[HttpPost]
public IActionResult CreateAjax(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return Json(new { success = false, message = "カテゴリ名は必須です。" });
}
var category = new IncomeCategory { Name = name.Trim() };
_context.IncomeCategories.Add(category);
_context.SaveChanges();
return Json(new { success = true, id = category.Id, name = category.Name });
}
[HttpGet]
public IActionResult GetAll()
{
var list = _context.IncomeCategories
.Select(c => new { id = c.Id, name = c.Name })
.ToList();
return Json(list);
}
改良ポイント
- 重複チェック (
/IncomeCategory/Exists) を追加し、同名登録を防止。 - SweetAlert2 のローディング表示で保存中や取得中の操作を防止。
- 関数分割で可読性・保守性を向上。
この形なら、
「+ 項目を追加」→ モーダル入力 → 重複チェック → 保存 → 最新一覧反映
という流れが安全かつ安定して動作します。
もし希望があれば、このコードを カテゴリ名の部分一致検索付きオートコンプリート に拡張して、入力時に候補を表示することも可能です。
そうすれば既存カテゴリの再利用がさらに簡単になります。
このオートコンプリート版も作りますか?
Powered by Froala Editor