DropDownListにコード・名前・区分を持たせる実装方法

システム開発
スポンサーリンク
スポンサーリンク

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-*属性から始めて、要件が複雑になったら他の手法に移行することをお勧めします。

システム開発
スポンサーリンク
tobotoboをフォローする

コメント

タイトルとURLをコピーしました