Webアプリケーションでよく遭遇する課題があります。
「商品を選択したら、その場で価格と在庫数を表示したい」
「部門コードを選んだら、対応する予算情報を自動取得したい」
「顧客名を選択すると、顧客区分も一緒に設定したい」
通常のDropDownListは「表示名」と「値」の2つしか持てませんが、実務ではこのような追加情報が必要になることが頻繁にあります。
本記事では、DropDownListに複数の属性を持たせる実装方法を、実用的なサンプルコードと共に解説します。
実装方法の選択ガイド
まず、どの手法を選ぶべきか判断基準を示します:
実装方法 | 難易度 | 適用場面 | 推奨度 |
---|---|---|---|
data-*属性 | ★☆☆ | 軽量な補足情報、リアルタイム処理 | 🌟🌟🌟 |
ASP.NET MVC | ★★☆ | サーバー主体、データ整合性重視 | 🌟🌟☆ |
Ajax連携 | ★★★ | 大量データ、動的取得が必要 | 🌟☆☆ |
✅ 選択フローチャート
データ量は少ない(50件以下)?
├─ YES → リアルタイム処理が必要?
│ ├─ YES → data-*属性を使用
│ └─ NO → ASP.NET MVCで十分
└─ NO → Ajax連携を検討
方法1:data-*属性を使った実装【推奨】
最も使用頻度が高く、実装も簡単な方法です。HTMLのdata-*
属性に情報を埋め込み、JavaScriptで取得します。
基本的な実装例
<!DOCTYPE html>
<html>
<head>
<title>商品選択サンプル</title>
<style>
.product-info {
margin: 10px 0;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
}
.hidden { display: none; }
</style>
</head>
<body>
<h2>商品選択</h2>
<label for="productSelect">商品を選択してください:</label>
<select id="productSelect">
<option value="">--選択してください--</option>
<option value="PRD001"
data-name="ノートPC"
data-category="電子機器"
data-price="89800"
data-stock="15">
ノートPC(PRD001)
</option>
<option value="PRD002"
data-name="マウス"
data-category="周辺機器"
data-price="2980"
data-stock="45">
マウス(PRD002)
</option>
<option value="PRD003"
data-name="キーボード"
data-category="周辺機器"
data-price="5500"
data-stock="28">
キーボード(PRD003)
</option>
</select>
<div id="productInfo" class="product-info hidden">
<h3>選択した商品の詳細</h3>
<p><strong>商品名:</strong><span id="displayName"></span></p>
<p><strong>カテゴリ:</strong><span id="displayCategory"></span></p>
<p><strong>価格:</strong><span id="displayPrice"></span>円</p>
<p><strong>在庫:</strong><span id="displayStock"></span>個</p>
</div>
<script>
document.getElementById('productSelect').addEventListener('change', function() {
const selectedOption = this.selectedOptions[0];
const infoDiv = document.getElementById('productInfo');
if (this.value === '') {
infoDiv.classList.add('hidden');
return;
}
try {
// data-*属性から情報を取得
document.getElementById('displayName').textContent = selectedOption.dataset.name || '不明';
document.getElementById('displayCategory').textContent = selectedOption.dataset.category || '不明';
document.getElementById('displayPrice').textContent = selectedOption.dataset.price || '0';
document.getElementById('displayStock').textContent = selectedOption.dataset.stock || '0';
infoDiv.classList.remove('hidden');
} catch (error) {
console.error('商品情報の表示でエラーが発生しました:', error);
infoDiv.classList.add('hidden');
}
});
</script>
</body>
</html>
応用例:カテゴリフィルター機能
<div>
<label for="categoryFilter">カテゴリで絞り込み:</label>
<select id="categoryFilter">
<option value="">--すべて表示--</option>
<option value="電子機器">電子機器</option>
<option value="周辺機器">周辺機器</option>
</select>
</div>
<div>
<label for="productSelect">商品選択:</label>
<select id="productSelect">
<option value="">--選択してください--</option>
<option value="PRD001" data-category="電子機器">ノートPC</option>
<option value="PRD002" data-category="周辺機器">マウス</option>
<option value="PRD003" data-category="周辺機器">キーボード</option>
</select>
</div>
<script>
document.getElementById('categoryFilter').addEventListener('change', function() {
const selectedCategory = this.value;
const productOptions = document.querySelectorAll('#productSelect option[data-category]');
productOptions.forEach(option => {
const optionCategory = option.dataset.category;
if (selectedCategory === '' || optionCategory === selectedCategory) {
option.style.display = '';
} else {
option.style.display = 'none';
}
});
// 商品選択をリセット
document.getElementById('productSelect').value = '';
});
</script>
方法2:ASP.NET MVCでの実装
サーバー側でデータ整合性を重視する場合や、大規模なアプリケーションでの実装方法です。
完全な実装例
Model(Product.cs)
public class Product
{
public string Code { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
ViewModel(ProductViewModel.cs)
public class ProductViewModel
{
public string SelectedProductCode { get; set; }
public string SelectedCategory { get; set; }
public List<Product> Products { get; set; } = new List<Product>();
// 選択された商品の詳細(表示用)
public Product SelectedProduct => Products.FirstOrDefault(p => p.Code == SelectedProductCode);
}
Controller(ProductController.cs)
public class ProductController : Controller
{
public ActionResult Index()
{
var model = new ProductViewModel
{
Products = GetProductList()
};
return View(model);
}
[HttpPost]
public ActionResult Index(ProductViewModel model)
{
if (ModelState.IsValid)
{
// 選択された商品とカテゴリを処理
var selectedProduct = GetProductList().FirstOrDefault(p => p.Code == model.SelectedProductCode);
if (selectedProduct != null)
{
ViewBag.Message = $"選択された商品: {selectedProduct.Name} (カテゴリ: {selectedProduct.Category})";
}
}
// 商品リストを再設定
model.Products = GetProductList();
return View(model);
}
private List<Product> GetProductList()
{
return new List<Product>
{
new Product { Code = "PRD001", Name = "ノートPC", Category = "電子機器", Price = 89800, Stock = 15 },
new Product { Code = "PRD002", Name = "マウス", Category = "周辺機器", Price = 2980, Stock = 45 },
new Product { Code = "PRD003", Name = "キーボード", Category = "周辺機器", Price = 5500, Stock = 28 }
};
}
}
View(Index.cshtml)
@model ProductViewModel
<h2>商品選択(ASP.NET MVC版)</h2>
@using (Html.BeginForm())
{
<div>
<label for="productSelect">商品を選択してください:</label>
<select id="productSelect" name="SelectedProductCode">
<option value="">--選択してください--</option>
@foreach (var product in Model.Products)
{
<option value="@product.Code"
data-category="@product.Category"
data-price="@product.Price"
data-stock="@product.Stock"
@(product.Code == Model.SelectedProductCode ? "selected" : "")>
@product.Name(@product.Code)
</option>
}
</select>
<input type="hidden" id="SelectedCategory" name="SelectedCategory" value="@Model.SelectedCategory" />
</div>
<div>
<input type="submit" value="選択内容を送信" class="btn btn-primary" />
</div>
@if (!string.IsNullOrEmpty(ViewBag.Message))
{
<div class="alert alert-info">
@ViewBag.Message
</div>
}
}
<!-- 選択された商品の詳細表示 -->
@if (Model.SelectedProduct != null)
{
<div class="product-details">
<h3>選択中の商品詳細</h3>
<p><strong>商品名:</strong>@Model.SelectedProduct.Name</p>
<p><strong>カテゴリ:</strong>@Model.SelectedProduct.Category</p>
<p><strong>価格:</strong>@Model.SelectedProduct.Price.ToString("N0")円</p>
<p><strong>在庫:</strong>@Model.SelectedProduct.Stock個</p>
</div>
}
<script>
document.getElementById('productSelect').addEventListener('change', function() {
const selectedOption = this.selectedOptions[0];
const categoryField = document.getElementById('SelectedCategory');
if (selectedOption.dataset.category) {
categoryField.value = selectedOption.dataset.category;
} else {
categoryField.value = '';
}
});
</script>
方法3:Ajax連携での実装
大量のデータを扱う場合や、動的にデータを取得する必要がある場合の実装方法です。
完全な実装例
HTML
<!DOCTYPE html>
<html>
<head>
<title>Ajax商品選択サンプル</title>
<style>
.loading { color: #666; font-style: italic; }
.error { color: #d32f2f; }
.product-info {
margin: 10px 0;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
}
.hidden { display: none; }
</style>
</head>
<body>
<h2>商品選択(Ajax版)</h2>
<div>
<label for="productSelect">商品を選択してください:</label>
<select id="productSelect">
<option value="">--選択してください--</option>
<option value="PRD001">ノートPC(PRD001)</option>
<option value="PRD002">マウス(PRD002)</option>
<option value="PRD003">キーボード(PRD003)</option>
</select>
</div>
<div id="productInfo" class="product-info hidden">
<div id="loadingMessage" class="loading hidden">商品情報を取得中...</div>
<div id="errorMessage" class="error hidden">商品情報の取得に失敗しました。</div>
<div id="productDetails" class="hidden">
<h3>商品詳細</h3>
<p><strong>商品名:</strong><span id="productName"></span></p>
<p><strong>カテゴリ:</strong><span id="productCategory"></span></p>
<p><strong>価格:</strong><span id="productPrice"></span>円</p>
<p><strong>在庫:</strong><span id="productStock"></span>個</p>
</div>
</div>
<script>
document.getElementById('productSelect').addEventListener('change', function() {
const productCode = this.value;
const infoDiv = document.getElementById('productInfo');
const loadingDiv = document.getElementById('loadingMessage');
const errorDiv = document.getElementById('errorMessage');
const detailsDiv = document.getElementById('productDetails');
// 初期状態に戻す
infoDiv.classList.add('hidden');
loadingDiv.classList.add('hidden');
errorDiv.classList.add('hidden');
detailsDiv.classList.add('hidden');
if (productCode === '') {
return;
}
// ローディング表示
infoDiv.classList.remove('hidden');
loadingDiv.classList.remove('hidden');
// Ajax で商品詳細を取得
fetch(`/api/products/${productCode}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(product => {
// 成功時の処理
loadingDiv.classList.add('hidden');
document.getElementById('productName').textContent = product.name;
document.getElementById('productCategory').textContent = product.category;
document.getElementById('productPrice').textContent = product.price.toLocaleString();
document.getElementById('productStock').textContent = product.stock;
detailsDiv.classList.remove('hidden');
})
.catch(error => {
// エラー時の処理
console.error('商品情報取得エラー:', error);
loadingDiv.classList.add('hidden');
errorDiv.classList.remove('hidden');
});
});
</script>
</body>
</html>
ASP.NET Core Web API(ProductsController.cs)
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private static readonly Dictionary<string, object> Products = new Dictionary<string, object>
{
{ "PRD001", new { name = "ノートPC", category = "電子機器", price = 89800, stock = 15 } },
{ "PRD002", new { name = "マウス", category = "周辺機器", price = 2980, stock = 45 } },
{ "PRD003", new { name = "キーボード", category = "周辺機器", price = 5500, stock = 28 } }
};
[HttpGet("{code}")]
public IActionResult GetProduct(string code)
{
if (Products.TryGetValue(code, out var product))
{
return Ok(product);
}
return NotFound(new { message = "商品が見つかりません。" });
}
}
視認性向上:optgroupを使ったグループ化
選択肢が多い場合、カテゴリごとにグループ化すると見やすくなります:
<select id="productSelect">
<optgroup label="電子機器">
<option value="PRD001">ノートPC</option>
<option value="PRD004">タブレット</option>
</optgroup>
<optgroup label="周辺機器">
<option value="PRD002">マウス</option>
<option value="PRD003">キーボード</option>
</optgroup>
</select>
ASP.NET MVCでの実装
<select name="SelectedProductCode">
@foreach (var group in Model.Products.GroupBy(p => p.Category))
{
<optgroup label="@group.Key">
@foreach (var product in group)
{
<option value="@product.Code">@product.Name</option>
}
</optgroup>
}
</select>
まとめ
DropDownListに複数属性を持たせる実装は、要件に応じて適切な手法を選択することが重要です:
- 軽量で即座に反応が必要 → data-*属性
- データ整合性と保守性を重視 → ASP.NET MVC
- 大量データや動的取得が必要 → Ajax連携
まずはdata-*属性から始めて、要件が複雑になったら他の手法に移行することをお勧めします。
コメント