0

I have two select boxes in my Blazor page. I select the department on Select Box-1 and the department related Machine Group on Select Box-2 (List of Select Box-2 will be loaded acc. to selection on Select-Box-1). In general it is working. But I have following problem: If I select Department (MFT) in SB-1 and select the 3. selection of Machine Group in SB-2 and then change the selection in SB-1 to another department: The correct list belonging to the new department is listed in SB-2, but I see directly the 3. selection of the SB-2's nwe selection list. Normaly I would expect that SB-2 should be reset to a default value (-- Select Machine Group--) How can I do that? In other words: How can I set the selection of a selection box with code to a default or predefined selection?

@page "/connect"
@using System.IO

<select class="Dep" @onchange="func_dep">
<option value="">-- Select Department --</option>
@foreach (var dept in templates_dep)
{
<option value=@dept>@dept</option>
}
</select>
<select class="MG" @onchange="func_MG">
<option value="">-- Select Machine Group --</option>
@foreach (var mgt in templates_MG)
{
<option value=@mgt>@mgt</option>
}
</select>

@code{

List<string> templates_dep = new List<string>() { "",""};

protected override async Task OnInitializedAsync()
{
templates_dep.Clear();    
read_dep(); 
}

public void read_dep()
{
var dep_file = File.ReadAllLines("files\\mae\\dep.csv");
foreach (var s in dep_file)
    templates_dep.Add(s);
}
}

@functions {

string selectedString_dep{get; set; }
List<string> templates_MG = new List<string>() { "", "", "", "", "" };
string selectedString_MG {get; set; }

async void func_dep(ChangeEventArgs e)
{
    templates_MG.Clear();    
    var path_mg ="files\\mae\\"+selectedString_dep+"_MG.csv";
    var logFile = File.ReadAllLines(path_mg);
    foreach (var s in logFile) templates_MG.Add(s);         
    
}

}

H H
  • 263,252
  • 30
  • 330
  • 514
Mdarende
  • 469
  • 3
  • 13
  • I have looked into the link in detail, but the critical point is that i have cascade working select boxes. And I want to bring the default value not on page load, but after each change of the previous select box. For my case I could not implement that solution. I didn't thought that it would be so complex to set the selection via code for a select box. – Mdarende Jun 28 '22 at 07:14
  • 1
    You have to understand it is not only on page load, as it is stated in the link, it implements a two way binding, with the use of onchange. Even if `value` is init value in html, Blazor rerenders so the behavior is not the same. – T.Trassoudaine Jun 28 '22 at 08:20
  • 1
    T.Trassoudaine You are absolutely right. I haved missed the "value" definition in the select part. It's working perfect. It's absolutely the easiest solution to implement. Thanks a lot :-) – Mdarende Jun 28 '22 at 15:14

3 Answers3

2

You can do it with two way binding and C# property setters :

<select @bind=@SelectedOptionA>
    <option value="">-- Select Department --</option>
    @foreach (var option in A_Options)
    {
        <option value="@option">@option</option>
    }
</select>
<select @bind=@selectedOptionB>
    <option value="">-- Select Machine Group--</option>
    @foreach (var option in B_Options)
    {
        <option value="@option">@option</option>
    }
</select>

@code {
    string selectedOptionA = "";
    string selectedOptionB = "";
    IEnumerable<string> A_Options = Enumerable.Range(start: 1, count: 10).Select(i => $"AOption-{i}");
    IEnumerable<string> B_Options = Enumerable.Empty<string>();

    public string SelectedOptionA
    {
        get => selectedOptionA;
        set
        {
            if (selectedOptionA != value)
            {
                selectedOptionA = value;
                if (value == "")
                {
                    selectedOptionB = "";
                    B_Options = Enumerable.Empty<string>();

                }
                else
                {
                    GetBOptions(value);

                    // Set Default Value
                    selectedOptionB = GetDefaultOption();
                }
            }
        }
    }

    private void GetBOptions(string value)
    {
        // Query real data here
        B_Options = Enumerable.Range(start: 1, count: 10)
                .Select(i => $"{value}/BOption-{i}")
                .ToList();
    }

    private string GetDefaultOption() 
        => B_Options.Skip(random.Next(minValue: 0, maxValue: 9)).First();

    Random random = new Random();
}
Brian Parker
  • 11,946
  • 2
  • 31
  • 41
  • My problem is that i have to use onChange instead of bind. The reason is, i have to change the selectbox list of the Select-Box-2, when the selection is changed on Selection-Box-1. Therefore I need to execute a code when the selection in Select-box1 changes. I thought it would be easy to set the "selection inmdex" of a selectbox via code, but if a see the suggestions... :) – Mdarende Jun 28 '22 at 07:19
  • @Mdarende are the load methods Async? I put the method inside the setter as an example but it would be query based using parameter from `value` wich is the new Select-Box-1 value. – Brian Parker Jun 28 '22 at 07:55
  • @Mdarende paste that into a page. I updated it to show how it reacts to the option change. – Brian Parker Jun 28 '22 at 08:04
1

Here's a demo system for a "Cascading Select" that I put together to answer a similar question on another site.

The demo page:

@page "/Test"
@page "/"

<PageTitle>Index</PageTitle>

<h6>Select Test</h6>

<div class="m-2 p-2 row">
    <div class="col-2">
        Continent:
    </div>
    <div class="col-6">
        <select class="form-select form-select-sm" value="@model.Continent" @onchange=this.ContinentChange>
            <option value="">-- Select A Continent --</option>
            @foreach (var continent in Data.Continents)
            {
                <option value="@continent.Name">@continent.Name</option>
            }
        </select>
    </div>
</div>

<div class="m-2 p-2 row">
    <div class="col-2">
        Country:
    </div>
    <div class="col-6">
        <select disabled="@this.countryDisabled" class="form-select form-select-sm" value="@model.Country" @onchange=this.CountryChange>
            <option value="">-- Select A Country --</option>
            @foreach (var item in filteredCountries)
            {
                <option value="@item.Name">@item.Name</option>
            }
        </select>
    </div>
</div>

<div class="m-2 p-2 bg-light">
    <div>
        Continent = @model.Continent
    </div>
    <div>
        Country = @model.Country
    </div>
</div>

@code {
    private CountryData Data = CountryData.Instance();
    private Model model = new Model();
    private List<Country> filteredCountries = new List<Country>();
    private bool countryDisabled => string.IsNullOrWhiteSpace(this.model.Continent);

    private void ContinentChange(ChangeEventArgs e)
    {
        string continent = e.Value?.ToString() ?? string.Empty;

        if (!model.Continent.Equals(continent, StringComparison.CurrentCultureIgnoreCase))
        {
            filteredCountries.Clear();
            filteredCountries.AddRange(Data.Countries.Where(item => item.Continent == continent));
            model.Country = string.Empty;
            model.Continent = continent;
        }
    }

    private void CountryChange(ChangeEventArgs e)
    {
        string country = e.Value?.ToString() ?? string.Empty;

        if (!model.Country.Equals(country, StringComparison.CurrentCultureIgnoreCase))
            model.Country = country;
    }

    public class Model
    {
        public string Country { get; set; } = string.Empty;
        public string Continent { get; set; } = string.Empty;
    }

}

And the data set it uses.

namespace BlazorApp3.Data;

public class Continent
{
    public string Name { get; set; } = String.Empty;
}

public class Country
{
    public string Continent { get; set; } = string.Empty;
    public string Name { get; set; } = String.Empty;
}

public class CountryData
{
    public IEnumerable<Country> Countries { get; private set; } = Enumerable.Empty<Country>();
    public IEnumerable<Continent> Continents { get; private set; } = Enumerable.Empty<Continent>();

    private CountryData()
        => this.GetData();

    public void GetData()
    {
        var continents = new List<Continent>();

        var continent = new Continent { Name = "Europe" };
        continents.Add(continent);
        var countries = new List<Country>
        {
            new Country { Name = "France", Continent = continent.Name },
            new Country { Name = "Portugal", Continent = continent.Name },
            new Country { Name = "England", Continent = continent.Name },
        };

        continent = new Continent { Name = "Africa" };
        continents.Add(continent);
        countries.Add(new Country { Name = "Senegal", Continent = continent.Name });
        countries.Add(new Country { Name = "Egypt", Continent = continent.Name });
        countries.Add(new Country { Name = "Kenya", Continent = continent.Name });

        continent = new Continent {Name = "South America" };
        continents.Add(continent);
        countries.Add(new Country { Name = "Brazil", Continent = continent.Name });
        countries.Add(new Country { Name = "Chile", Continent = continent.Name });
        countries.Add(new Country { Name = "Peru", Continent = continent.Name });

        this.Continents = continents;
        this.Countries = countries;
    }

    public static CountryData? _instance;
    public static CountryData Instance()
    {
        if (_instance is null)
            _instance = new CountryData();

        return _instance;
    }
}
MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
0

I found it easiest to use IJSRuntime and call a pure javascript function to reset it.
For example:

Here is the javascript function:

function resetSelectElement(id) {
    var selectElement = document.getElementById(id);
    selectElement.selectedIndex = 0;  // first option is selected, or
    // -1 for no option selected
}

Here is how the javascript function is called from the razor page:

//contruct
@inject IJSRuntime jsRuntime

//call inside a method
await jsRuntime.InvokeVoidAsync("resetSelectElement", "mySelectId");
Cobysan
  • 99
  • 6