Search code examples
c#blazorblazor-server-side

Using Blazor, rendering <select> elements in a for loop and can't get @bind to work with a dictionary by index


Let's define the dictionary I'm using as searchBoardGames[image.Id]

The select is in a foreach loop so I can render multiple select elements because what I'm trying to do is collect data from the user for multiple entries, but using the same list of gameIds. The @foreach loop with the <select> would be something like this:

<div class="image-gallery">
    @foreach (var image in images)
    {
        <div class="image-item">
            <img src="data:image/png;base64,@Convert.ToBase64String(image.ImageData)" alt="Unknown Image" />
            <div id="boardGameSelect" class="board-game-select">
                @if (searchResults.ContainsKey(image.Id) && searchResults[image.Id].Any())
                {
                    <select id="@selectInputId" @bind="searchBoardGames[image.Id]">
                        <option value="">-- Select a Board Game --</option>
                        @foreach (var game in searchResults[image.Id])
                        {
                            <option value="@game.Id">@game.Name</option>
                        }
                    </select>
                }
            </div>
        </div>
    }
</div>

So now the problem is the @bind=searchBoardGames[image.Id] isn't going to work, but I can't figure out how to get around this problem. I can't add an @onchange because that's used by @bind and I haven't been able to construct a method that could return something because it needs to be bound to a Property, not the value of some returned variable.


Solution

  • You can use @onchange to manually handle the changes for each <select>, since @bind can't directly bind to a dictionary key. A whole working demo you could follow:

    @page "/"
    @rendermode InteractiveServer
    <form method="post" @onsubmit="HandleValidSubmit" @formname="TestForm">
        <div class="image-gallery">
            @foreach (var image in images)
            {
                <div class="image-item">
                    <img src="data:image/png;base64,@Convert.ToBase64String(image.ImageData)" alt="Unknown Image" />
                    <div id="boardGameSelect" class="board-game-select">
                        @if (searchResults.ContainsKey(image.Id) && searchResults[image.Id].Any())
                        {
                            <select id="select-@image.Id" value="@GetSelectedValue(image.Id)" @onchange="(e) => UpdateSelectedGame(image.Id, e.Value)">
                                <option value="">-- Select a Board Game --</option>
                                @foreach (var game in searchResults[image.Id])
                                {
                                    <option value="@game.Id">@game.Name</option>
                                }
                            </select>
                        }
                    </div>
                </div>
            }
        </div>
    
        <button type="submit">Submit</button>
    </form>
    
    @code {
        // Dictionary to hold the selected board game for each image
        private Dictionary<int, string> searchBoardGames = new Dictionary<int, string>();
    
        // List of images (you can replace this with your actual model for images)
         List<ImageModel> images = new List<ImageModel>
        {
            new ImageModel { Id = 1, ImageData =new byte[0] }, // Sample image
            new ImageModel { Id = 2, ImageData = new byte[0]}   // Sample image
        };
    
        // Dictionary that stores search results for board games per image ID
        private Dictionary<int, List<GameModel>> searchResults = new Dictionary<int, List<GameModel>>
        {
            { 1, new List<GameModel> { new GameModel { Id = "g1", Name = "Catan" }, new GameModel { Id = "g2", Name = "Ticket to Ride" } } },
            { 2, new List<GameModel> { new GameModel { Id = "g3", Name = "Pandemic" }, new GameModel { Id = "g4", Name = "Carcassonne" } } }
        };
    
        // Method to get the selected value for the current image
        private string GetSelectedValue(int imageId)
        {
            if (searchBoardGames.ContainsKey(imageId))
            {
                return searchBoardGames[imageId];
            }
            return "";
        }
    
        // Method to update the selected board game for an image
        private void UpdateSelectedGame(int imageId, object selectedGameId)
        {
            if (!string.IsNullOrEmpty(selectedGameId?.ToString()))
            {
                searchBoardGames[imageId] = selectedGameId.ToString();
            }
        }
        private void HandleValidSubmit()
        {
            // Process the selected board games
            foreach (var entry in searchBoardGames)
            {
                Console.WriteLine($"Image {entry.Key} has selected game ID: {entry.Value}");
            }
        }
        // Sample models
        public class ImageModel
        {
            public int Id { get; set; }
            public byte[] ImageData { get; set; }
        }
    
        public class GameModel
        {
            public string Id { get; set; }
            public string Name { get; set; }
        }
    }