Blazor的InputSelect onchange事件不起作用。

4

我正在使用 Blazor 服务器应用程序,尝试使用 @onchange="countyClicked" 事件根据国家下拉列表的更改来填充城市下拉列表,并将下拉列表绑定到模型。

现在的问题是 onchange 事件不起作用,国家下拉列表更改时城市下拉列表不会填充。

以下是我的下拉列表组件 HTML:

<InputSelect @bind-Value="PersonModel.CountryId" class="form-control" @onchange="countyClicked">
   <option value="">Select country</option>
   @foreach (var item in Countries)
   {
      <option value="@item.CountryId">@item.CountryName</option>
   }
</InputSelect>

<InputSelect class="form-control mb-2 mr-sm-2" @bind-Value="PersonModel.CityId">
   @foreach (var city in Cities)
   {
      <option value="@city.CityId">@city.CityName</option>
   }
</InputSelect>
<br><br>



public void countyClicked(ChangeEventArgs args)
{
   var getCountryId = args.Value.ToString();
   int.TryParse(getCountryId, out int countryId);
   Cities = mainService.GetAllCityByCountryId(countryId);
}

也许您需要在调用mainService后使用StateHasChanged(),以刷新第二个输入选择框。 - dani herrera
<InputSelect> is not a Html Element rather it's a component thus @onchange will not work here. Either you need to make use of C# to perform a cascading change while using two way binding with @bind-Value or you need to use ValueChanged which works similar to onchange - Suprabhat Biswal
2个回答

7
注意: @onchange 是一个与 Html 元素一起使用的编译器指令,不适用于 Razor 组件。然而,InputSelect 组件在内部使用 change 事件。
以下代码示例演示了如何填充两个 InputSelect 组件,它们如何相互作用以及它们如何与表单的其余部分交互。请注意,我使用验证来强制用户选择一个国家和一个城市,否则表单将无法“提交”。 将代码复制到您的 Index 页面中并进行测试。
@page "/"


@using System.ComponentModel.DataAnnotations;

<EditForm EditContext="@EditContext" OnValidSubmit="HandleValidSubmit"
          Context="NewContext">
    <DataAnnotationsValidator />

    <div class="form-group">
        <label for="name">Enter your Name: </label>
        <InputText Id="name" Class="form-control" @bind-Value="@person.Name">
        </InputText>
        <ValidationMessage For="@(() => person.Name)" />

    </div>
    <div class="form-group">
        <label for="body">Select your country: </label>
        <InputSelect ValueExpression="@(() => person.CountryID)"
                     Value="@person.CountryID"
                     ValueChanged="@((int? args) => { person.CountryID = args; SelectCities(args); })">
            <option value="">Select country...</option>
            @foreach (var country in countryList)
            {
                <option value="@country.ID">@country.Name</option>
            }
        </InputSelect>
        <ValidationMessage For="@(() => person.CountryID)" />
    </div>
    @if (cityList != null && cityList.Any())
    {
    <div class="form-group">
        <label for="body">Select your city: </label>
        <InputSelect ValueExpression="@(() => person.CityID)"
                     Value="@person.CityID" ValueChanged="@((int? args) =>
                                                person.CityID = args)">
            <option value="">Select city...</option>
            @foreach (var city in cityList)
            {
                <option value="@city.ID">@city.Name</option>
            }
        </InputSelect>
        <ValidationMessage For="@(() => person.CityID)" />
    </div>
    }
    <p>
        <button type="submit">Submit</button>
    </p>
</EditForm>


@code
    {

    private EditContext EditContext;
    private Person person = new Person() { };

    private List<Country> countryList = new List<Country>
    {
        new Country
        {
            ID = 1, Name="USA",
            Cities = new List<City> {  new City { ID = 1, Name = "City1" },
                        new City { ID = 2, Name = "City2" } }
        },
        new Country
        {
            ID =2, Name="Germany",
            Cities =  new List<City> {  new City { ID = 3, Name = "City3" },
                        new City { ID = 4, Name = "City4" } }
        },
       new Country {ID =3, Name="France",
            Cities = new List<City>  {  new City { ID = 5, Name = "City5" },
                        new City { ID =6, Name = "City6" } } },
       new Country {ID =4, Name="UK",
           Cities = new List<City>  {
                       new City { ID = 7, Name = "City7" },
                       new City { ID =8, Name = "City8" },
                       new City { ID = 9, Name = "City9" },
                       new City { ID = 10, Name = "City10" }} },
       new Country {ID =5, Name="Russia",
           Cities =  new List<City>  {  new City { ID = 11, Name = "City11" } }}
    };


    private IQueryable<City> cityList;

    private void HandleValidSubmit()
    {
        Console.WriteLine("Submitting");
    }
    protected override void OnInitialized()
    {
        EditContext = new EditContext(person);

        base.OnInitialized();
    }


    private void SelectCities(int? id)
    {
        person.CityID = null;

        cityList = (from country in countryList
                    where country.ID == id
                    from city in country.Cities
                    select city).AsQueryable();
    }

    public class Country
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public List<City> Cities { get; set; }
    }

    public class City
    {
        public int ID { get; set; }
        public string Name { get; set; }


    }

    public class Person
    {
        public int ID { get; set; }

        [Required]
        public string Name { get; set; }

        [Required(ErrorMessage ="Please, select your country.")]
        public int? CountryID { get; set; }
        [Required(ErrorMessage = "Please, select your city.")]
        public int? CityID { get; set; }

    }

} 

更新:

ValueChanged="@((int? args) => { person.CountryID = args; SelectCities(args); })" do

该段代码为C#中的语句,其中 "ValueChanged" 属性绑定了一个匿名方法,该方法接收一个可空整型参数 "args" ,然后将其赋值给 "person.CountryID" ,最后调用方法 "SelectCities(args)"。
ValueChanged is defined in the InputSelect component's class as an EventCallback. When you select an item in the select element, the `change` event is triggered. In JavaScript you can access the newly selected value through the event object. In Blazor (JSInterop is behind the scene), you code is passed the selected value in the parameter args (int? args), and your code should assign it to the bound filed (person.CountryID). This is equivalent to what you were trying to do by using `@onchange="countyClicked"`, which is correct when you use html element, but the InputSelect is a component, not html element.

SelectCities(args)是一个方法,当您在国家InputSelect中选择一个值时调用该方法,其参数是所选值,即所选国家的id,并且该方法的作用是根据所选国家筛选城市。

阅读这个问题:When to use ValueChanged and ValueExpression in Blazor? 以及我的回答。 查看我的答案:here。 请参考我在这里这里的答案。


非常感谢,它对我有效。还有一个请求,您能告诉我这里的值(value)、值表达式(value expression)和值更改(valuechanged)是什么吗?我已经搜索了它们,但没有理解太多,ValueChanged="@((int? args) => { person.CountryID = args; SelectCities(args); })"是什么意思? - sudip chand
谢谢,伙计。这是我在 Stack Overflow 上看到的四个不同答案中唯一一个对我有效的。 - undefined

7

将您的编辑表单更改为使用包含模型的编辑上下文。

<EditForm  EditContext="EditContext">
@*You don't have to write a onchange becuase your EditContext_OnFieldChanged will catch your changes*@
<InputSelect @bind-Value="PersonModel.CountryId" class="form-control">
            <option value="">Select country</option>
            @foreach (var item in Countries)
            {
                <option value="@item.CountryId">@item.CountryName</option>
            }
        </InputSelect>

        <InputSelect class="form-control mb-2 mr-sm-2" @bind-Value="PersonModel.CityId">
            @foreach (var city in Cities)
            {
                <option value="@city.CityId">@city.CityName</option>
            }
        </InputSelect>
</<EditForm  >

通过使用 OnFieldChanged 事件,您可以订阅表单中任何模型更改。

private EditContext EditContext;

private YourModel PersonModel= new YourModel();

protected override void OnInitialized()
{
    //Don't forget to assign a value to your PersonModel!
    EditContext = new EditContext(PersonModel);

    EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}

private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
    // identify field, retreive the set value from the model and populate the cities collection
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接