Blazor Binding父/子/子下拉doctuns无法在子控制器控制上设置默认值

发布于 2025-02-07 13:23:52 字数 2444 浏览 1 评论 0原文

在父/孩子下拉场景中,我每次更新父级下拉次数时都试图显示子级别下拉词的默认值。

该代码可以完成该作业,因为它更新了示例标签,但是子下拉菜没有显示任何内容。

为了简单起见,我正在用一个国家/区域对的选定控件来演示问题。 初始渲染正确显示了该国的默认国家和默认区域。 更新国家 /地区在标签上设置了新的默认区域,但是“区域选择”变为空白:

@page "/test"
    <select @bind="@CountryName">
        @foreach (var country in Countries)
        {
            <option>@country.Name</option>
        }
    </select>

    <select @bind="@ZoneName">
        @foreach (string zone in Zones)
        {
            <option>@zone</option>
        }
    </select>

    <div>Selected country: @CountryName</div>
    <div>Selected zone: @ZoneName</div>

@code {
    private List<Country> Countries;
    private List<String> Zones = new();

    private string countryName;
    private string CountryName
    {
        get => countryName;
        set
        {
            countryName = value;
            var country = Countries.First(x => x.Name == value);
            Zones = country.Zones.Select(x => x.Name).ToList();
            ZoneName = country.DefaultZoneName;
        }
    }

    private string zoneName;
    private string ZoneName
    {
        get => zoneName;
        set
        {
            zoneName = value;
        }
    }

    protected override void OnInitialized()
    {
        //data factory

        var barcelona = new Zone() { Name = "Barcelona" };
        var madrid = new Zone() { Name = "Madrid" };
        var spain = new Country() { Name = "Spain" };
        spain.Zones.Add(barcelona);
        spain.Zones.Add(madrid);
        spain.DefaultZoneName = spain.Zones.Last().Name;;

        var açores = new Zone() { Name = "Açores" };
        var algarve = new Zone() { Name = "Algarve" };
        var portugal = new Country() { Name = "Portugal" };
        portugal.Zones.Add(açores);
        portugal.Zones.Add(algarve);
        portugal.DefaultZoneName = portugal.Zones.Last().Name;

        Countries = new List<Country>();
        Countries.Add(spain);
        Countries.Add(portugal);
        CountryName = Countries.Last().Name;
        base.OnInitialized();
    }

    //models

    public class Country
    {
        public string Name { get; set; }
        public List<Zone> Zones { get; set; } = new();
        public string DefaultZoneName { get; set; }
    }

    public class Zone
    {
        public string Name { get; set; }
    }
}

On a parent/child dropdown scenario, I'm trying to display a default value for the child dropdown each time the parent dropdown is updated.

The code does the job, since it updates the sample label, but the child dropdown does not display anything.

For simplicity I am demonstrating the issue with a country/zone pair of select controls.
Initial render correctly shows the default country and the default zone of the country.
Updating the country sets the new default zone on the label but the zones select turns blank:

@page "/test"
    <select @bind="@CountryName">
        @foreach (var country in Countries)
        {
            <option>@country.Name</option>
        }
    </select>

    <select @bind="@ZoneName">
        @foreach (string zone in Zones)
        {
            <option>@zone</option>
        }
    </select>

    <div>Selected country: @CountryName</div>
    <div>Selected zone: @ZoneName</div>

@code {
    private List<Country> Countries;
    private List<String> Zones = new();

    private string countryName;
    private string CountryName
    {
        get => countryName;
        set
        {
            countryName = value;
            var country = Countries.First(x => x.Name == value);
            Zones = country.Zones.Select(x => x.Name).ToList();
            ZoneName = country.DefaultZoneName;
        }
    }

    private string zoneName;
    private string ZoneName
    {
        get => zoneName;
        set
        {
            zoneName = value;
        }
    }

    protected override void OnInitialized()
    {
        //data factory

        var barcelona = new Zone() { Name = "Barcelona" };
        var madrid = new Zone() { Name = "Madrid" };
        var spain = new Country() { Name = "Spain" };
        spain.Zones.Add(barcelona);
        spain.Zones.Add(madrid);
        spain.DefaultZoneName = spain.Zones.Last().Name;;

        var açores = new Zone() { Name = "Açores" };
        var algarve = new Zone() { Name = "Algarve" };
        var portugal = new Country() { Name = "Portugal" };
        portugal.Zones.Add(açores);
        portugal.Zones.Add(algarve);
        portugal.DefaultZoneName = portugal.Zones.Last().Name;

        Countries = new List<Country>();
        Countries.Add(spain);
        Countries.Add(portugal);
        CountryName = Countries.Last().Name;
        base.OnInitialized();
    }

    //models

    public class Country
    {
        public string Name { get; set; }
        public List<Zone> Zones { get; set; } = new();
        public string DefaultZoneName { get; set; }
    }

    public class Zone
    {
        public string Name { get; set; }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

盗梦空间 2025-02-14 13:23:52

问题在于,set变量上的方法不会导致页面重新渲染:自动重新渲染在处理@event的任务时发生,并强制重新恢复呈现<代码> statehaschanged 发生。

您正在尝试使用set方法当选择值时。但是,这是一个陷阱。 Blazor允许C#布尔值选择(以及disabledhidden之类的其他事物)。

您已经花了一些时间来很好地设置数据,因此您可以通过参考跟踪选择,而不是通过将名称的字符串值复制到新变量来进行选择。

我认为以下是一个改进。我希望它能为您提供帮助,因为大餐经常出现这种情况:

@page "/"

    <select @onchange="(args) => SelectedCountry = Countries.First(x => x.Name==args.Value?.ToString())">
        @foreach (var country in Countries)
        {
            <option selected="@(country == SelectedCountry)" >@country.Name</option>
        }
    </select>

    <select @onchange="(args) => SelectedCountry.SelectedZone = SelectedCountry.Zones.First(x => x.Name==args.Value?.ToString())">
        @foreach (var zone in SelectedCountry.Zones)
        {
            <option selected="@(zone == SelectedCountry.SelectedZone)">@zone.Name</option>
        }
    </select>

    <div>Selected country: @SelectedCountry.Name</div>
    <div>Selected zone: @SelectedCountry?.SelectedZone?.Name</div>

@code {
    private List<Country> Countries = new();
    private Country SelectedCountry = new();

    protected override void OnInitialized()
    {
        //data factory

        var barcelona = new Zone() { Name = "Barcelona" };
        var madrid = new Zone() { Name = "Madrid" };
        var spain = new Country() { Name = "Spain" };
        spain.Zones.Add(barcelona);
        spain.Zones.Add(madrid);
        spain.SelectedZone = spain.Zones.Last();

        var açores = new Zone() { Name = "Açores" };
        var algarve = new Zone() { Name = "Algarve" };
        var portugal = new Country() { Name = "Portugal" };
        portugal.Zones.Add(açores);
        portugal.Zones.Add(algarve);
        portugal.SelectedZone = portugal.Zones.Last();

        Countries = new List<Country>();
        Countries.Add(spain);
        Countries.Add(portugal);
        SelectedCountry = portugal;
    }

    //models

    public class Country
    {
        public string Name { get; set; }
        public List<Zone> Zones { get; set; } = new();
        public Zone? SelectedZone;
    }

    public class Zone
    {
        public string Name { get; set; }
    }
}

The problem is that set method on variable doesn't cause the page to re-render: automatic re-render occurs with a Task handling an @event, and forced re-render occurs with StateHasChanged.

You are trying to use set method because 2-way binding on the select consumes the @onchange event, so you have no way to do anything when a value is selected. However, this is a trap. Blazor allows C# boolean for selected (and also for other things like disabled or hidden).

You already took some time to set up your data nicely, so you can track selection by reference, not by copying the string values of names to new variables.

I think the following is an improvement. I hope it helps you, as this same situation will come up very often in Blazor:

@page "/"

    <select @onchange="(args) => SelectedCountry = Countries.First(x => x.Name==args.Value?.ToString())">
        @foreach (var country in Countries)
        {
            <option selected="@(country == SelectedCountry)" >@country.Name</option>
        }
    </select>

    <select @onchange="(args) => SelectedCountry.SelectedZone = SelectedCountry.Zones.First(x => x.Name==args.Value?.ToString())">
        @foreach (var zone in SelectedCountry.Zones)
        {
            <option selected="@(zone == SelectedCountry.SelectedZone)">@zone.Name</option>
        }
    </select>

    <div>Selected country: @SelectedCountry.Name</div>
    <div>Selected zone: @SelectedCountry?.SelectedZone?.Name</div>

@code {
    private List<Country> Countries = new();
    private Country SelectedCountry = new();

    protected override void OnInitialized()
    {
        //data factory

        var barcelona = new Zone() { Name = "Barcelona" };
        var madrid = new Zone() { Name = "Madrid" };
        var spain = new Country() { Name = "Spain" };
        spain.Zones.Add(barcelona);
        spain.Zones.Add(madrid);
        spain.SelectedZone = spain.Zones.Last();

        var açores = new Zone() { Name = "Açores" };
        var algarve = new Zone() { Name = "Algarve" };
        var portugal = new Country() { Name = "Portugal" };
        portugal.Zones.Add(açores);
        portugal.Zones.Add(algarve);
        portugal.SelectedZone = portugal.Zones.Last();

        Countries = new List<Country>();
        Countries.Add(spain);
        Countries.Add(portugal);
        SelectedCountry = portugal;
    }

    //models

    public class Country
    {
        public string Name { get; set; }
        public List<Zone> Zones { get; set; } = new();
        public Zone? SelectedZone;
    }

    public class Zone
    {
        public string Name { get; set; }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文