如何在 Blazor 表单中维护多个级联下拉菜单的单独状态?

问题描述 投票:0回答:1

链接到 GitHub 以快速访问最小可重现示例(下面也提供了代码):https://github.com/thecodeiackiller/cascadingSelectListProblem

  • #1) 我有一份锻炼清单。
  • #2) 我有 2 个枚举(WorkoutType 和ExerciseType)来过滤锻炼列表。
  • #3) 我有一个 EditForm,它允许用户: 选择锻炼类型(下半身或上半身),它会过滤锻炼列表并 选择锻炼类型(主要、次要、附件),这会进一步过滤列表。

注意:对于那些不熟悉健身领域的人,在给定的锻炼中,您通常有一种锻炼类型(腿部训练、上半身等),并且在该锻炼中,您有锻炼类型(您的主要/较重锻炼、您的辅助练习和辅助练习(锻炼小肌肉)。

预期用户流量:

    1. 选择了 WorkoutType 并过滤了练习列表(我相信我的表单已经成功做到了这一点)。
    1. 对于 EditForm 中的第一行,用户选择锻炼类型(主要、次要、附件),并再次过滤列表。
    1. 用户为现在经过两次过滤的列表选择练习的名称。还在第一排。
    1. 用户导航到第二行。
    1. 用户选择锻炼类型
    1. 用户从列表中选择新的锻炼名称(应根据第二行所选的锻炼类型进行过滤)。

问题:第二行的练习名称列表不会更新。尽管练习类型不同,练习名称列表仍与第一个练习相同,这应该会更改显示的练习。

我当前实现的

AddWorkout.razor组件:

@using stateManagementMinimumReproducibleExample.Repositories
@using stateManagementMinimumReproducibleExample.Models
@page "/addworkout"
@rendermode InteractiveServer
@inject UserExerciseRepository userExerciseRepository

<EditForm Model="userExercise">
    <label for="workoutType"> Workout Type: </label>
    <InputSelect id="workoutType" @bind-Value="userExercise.WorkoutType" @bind-Value:after="GetExercisesByWorkoutType">
        @foreach (var type in Enum.GetValues(typeof(WorkoutType)))
        {
            <option value="@type">@type</option>
        }
    </InputSelect>

    @for (int i = 0; i < numberOfExercisesInWorkout; i++)
    {
        var j = i;
        if (j >= exerciseListForSelectedDay.Count)
        {
            exerciseListForSelectedDay.Add(new UserExercise());
        }

        <div>
            <label for="exerciseType"> Exercise Type: </label>
            <InputSelect id="exerciseType" @bind-Value="exerciseListForSelectedDay[j].ExerciseType" @bind-Value:after="GetExercisesByExerciseType">
                @foreach (var type in Enum.GetValues(typeof(ExerciseType)))
                {
                    <option value="@type">@type</option>
                }
            </InputSelect>

            <label for="exerciseName"> Exercise: </label>
            <InputSelect id="exerciseName" @bind-Value="exerciseListForSelectedDay[j].ExerciseId">
                @foreach (var exercise in listOfExerciseByExerciseType)
                {
                    <option value="@exercise.Id">@exercise.Name</option>
                }
            </InputSelect>
        </div>
    }
</EditForm>


@code {
    private int numberOfExercisesInWorkout = 2;

    private UserExercise userExercise = new();

    private List<UserExercise> exerciseListForSelectedDay = new();

    public List<Exercise> listOfExercisesByWorkoutType = new();

    public List<Exercise> listOfExerciseByExerciseType = new();

    public async Task GetExercisesByWorkoutType()
    {
            listOfExercisesByWorkoutType = await   userExerciseRepository.GetExercisesBasedOnWorkoutType(userExercise.WorkoutType);
            StateHasChanged();
    }

    public void GetExercisesByExerciseType()
    {
            listOfExerciseByExerciseType = userExerciseRepository.GetExercisesFromWorkoutListBasedOnExerciseType(listOfExercisesByWorkoutType, userExercise.ExerciseType);;
    }
}

我考虑过创建一个字典来存储锻炼中每个练习的练习列表的可能性。此外,我考虑过将列表存储为 UserExercise 类中的属性,然后将筛选后的练习列表添加到该属性中。我不确定我到底应该走哪条路,或者是否有任何潜在的特定于 Blazor 的状态管理替代方案。

上面的代码应该为您提供足够的信息来诊断问题。如果您需要视觉效果,请按照以下步骤在分叉和克隆 GitHub 存储库后快速重现问题:

  • 启动应用程序
  • 导航到 /addworkout 端点
  • 选择锻炼类型(对于整个表格)
  • 选择锻炼类型(第一行)
  • 选择一个练习(第一行)
  • 选择锻炼类型(第二行)
  • 选择一个练习(第二行)

此时,您将看到基于选择与第一行不同的锻炼类型,第二行的过滤列表尚未更新。我怎样才能获得第二次(及以后)出现的适当练习列表? 谢谢你。

blazor blazor-server-side cascadingdropdown blazor-editform
1个回答
0
投票

您可以尝试以下代码来解决该问题:

添加锻炼.razor:

@using stateManagementMinimumReproducibleExample.Repositories
@using stateManagementMinimumReproducibleExample.Models
@page "/addworkout"
@rendermode InteractiveServer
@inject UserExerciseRepository userExerciseRepository

<EditForm Model="userExercise">
    <label for="workoutType"> Workout Type: </label>
    <InputSelect id="workoutType" @bind-Value="userExercise.WorkoutType" @bind-Value:after="GetExercisesByWorkoutType">
        @foreach (var type in Enum.GetValues(typeof(WorkoutType)))
        {
            <option value="@type">@type</option>
        }
    </InputSelect>

    @for (int i = 0; i < numberOfExercisesInWorkout; i++)
    {
        var j = i;
        if (j >= exerciseListForSelectedDay.Count)
        {
            exerciseListForSelectedDay.Add(new UserExercise());
        }

        <div>
            <label for="exerciseType"> Exercise Type: </label>
            <InputSelect id="exerciseType" @bind-Value="exerciseListForSelectedDay[j].ExerciseType" @bind-Value:after="() => GetExercisesByExerciseType(j)">
                @foreach (var type in Enum.GetValues(typeof(ExerciseType)))
                {
                    <option value="@type">@type</option>
                }
            </InputSelect>

            <label for="exerciseName"> Exercise: </label>
            <InputSelect id="exerciseName" @bind-Value="exerciseListForSelectedDay[j].ExerciseId">
                @foreach (var exercise in exerciseListForSelectedDay[j].FilteredExercises)
                {
                    <option value="@exercise.Id">@exercise.Name</option>
                }
            </InputSelect>
        </div>
    }
</EditForm>

@code {
    private int numberOfExercisesInWorkout = 2;
    private UserExercise userExercise = new();
    private List<UserExercise> exerciseListForSelectedDay = new();

    public List<Exercise> listOfExercisesByWorkoutType = new();

    public async Task GetExercisesByWorkoutType()
    {
        listOfExercisesByWorkoutType = await userExerciseRepository.GetExercisesBasedOnWorkoutType(userExercise.WorkoutType);
        // Update each exercise list based on the new workout type
        foreach (var exercise in exerciseListForSelectedDay)
        {
            exercise.FilteredExercises = userExerciseRepository.GetExercisesFromWorkoutListBasedOnExerciseType(listOfExercisesByWorkoutType, exercise.ExerciseType);
        }
        StateHasChanged();
    }

    public async Task GetExercisesByExerciseType(int index)
    {
        // Filter the exercises based on the ExerciseType for the specific row (index)
        var selectedExerciseType = exerciseListForSelectedDay[index].ExerciseType;
        exerciseListForSelectedDay[index].FilteredExercises = userExerciseRepository.GetExercisesFromWorkoutListBasedOnExerciseType(listOfExercisesByWorkoutType, selectedExerciseType);
        await InvokeAsync(StateHasChanged);
    }
}

UserExercise.cs:

namespace stateManagementMinimumReproducibleExample.Models
{
    public class UserExercise
    {
        public int Id { get; set; }
        public int ExerciseId { get; set; }
        public ExerciseType ExerciseType { get; set; }
        public WorkoutType WorkoutType { get; set; }

        // Add this property to store the filtered exercises for each row
        public List<Exercise> FilteredExercises { get; set; } = new List<Exercise>();
    }

}

所以基本上,在您的代码中,您尝试全局更新列表,并且它使用单个列表,因此您看到了相同的练习。为了避免这个问题,您可以为行创建单独的过滤器列表。

结果:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.