当参数改变时如何重新渲染 Blazor 组件

7

我有两个Blazor组件。 第一个组件只显示来自JSON api的学生名单。

<select @onchange="selectStudent"> 
  @foreach(var student in students) {
    <option value="@student.id"> @student.name /option>
  }
</select>

@code {
    var API = "https://abcd.com/students/"
    students = // variable stores JSON data of students used in the foreach loop


    // sending select event to parent
    [Parameter]
    public EventCallBack<string> OnStudentSelect { get; set; }
    public async Task SelectStudent(ChangeEventArgs e) {
        await OnStudentSelect.InvokeAsync(e.Value.ToString())
    }
}

当用户从下拉列表中选择一个学生时,我想要捕获该学生的ID,并将其作为参数发送到另一个组件。
@page "/student"

<Students OnStudentSelect="@GetStudentId"> </Students>     

<p> Displaying profile of StudentID: @studentId </p>
<Student StudentId="@StudentId"> </Student>

@code{
    private int StudentId = 1; 

    private void GetStudentId(int _id) {
        studentId = _id
    }
}

这是我的代码片段,它能够运行并且我可以看到消息在<p></p>标签中发生了变化。

我遇到的问题是:

<Student StudentId="StudentId"> </Student>

由于某些原因,当组件接收到新的StudentId时,它不会更新。

StateHasChanged()的文档不够清晰,但将其放在Student内部似乎也无法解决问题。


1
你的意思是将其放置在Student内部吗?如果你想要更新组件,你可以将StateHasChanged()放置在你的GetStudentId()函数中,例如在student = _id之后。 - Roland Deschain
@PatrickBeynio 我是个初学者,我认为我们都曾经历过这个阶段。别着急。 - elektra
@RolandDeschain 我的意思是将 StateHasChanged(); 放在 Student 组件内部不会刷新组件。 - elektra
1
你的代码仍然不一致,因此我们无法看到真正的问题 - 使用 https://blazorrepl.telerik.com/ 或任何你喜欢的工具创建一个可重现的示例。 - Mister Magoo
1
我认为你的问题非常清晰,第一次阅读就能理解。干杯! - Flood
显示剩余3条评论
2个回答

13

阅读了您的帖子和评论后,我决定编写一些组件,这些组件是我觉得能够回答您的问题并向您展示未来编写Blazor组件的好方法。让我们从第一个组件开始,即StudentSelector.razor:

StudentSelector.razor

@using BlazorAnswers.Models

@*Show loading content while students are loading*@
@if(!StudentList.Any())
{
    @LoadingContent
}
else
{
    <label>Select a Student: </label>
    <select @onchange="HandleStudentChanged" class="form-select w-25">
        @foreach (var student in StudentList)
        {
            <option value="@student.Id">@student.Name</option>
        }
    </select>
}

@code {
    [Parameter] public EventCallback<StudentModel> OnStudentChanged { get; set; }

    [Parameter] public IEnumerable<StudentModel> StudentList { get; set; } = Enumerable.Empty<StudentModel>();

    [Parameter] public RenderFragment LoadingContent { get; set; }

    /// <summary>
    /// This transforms the ChangeEventArgs into a StudentModel
    /// </summary>
    private async Task HandleStudentChanged(ChangeEventArgs args)
    {
        if (OnStudentChanged.HasDelegate)
        {
            if (args is not null && int.TryParse((string)args.Value, out var id))
            {
                var selectedStudent = StudentList.FirstOrDefault(a => a.Id.Equals(id));

                if (selectedStudent is not null)
                    await OnStudentChanged.InvokeAsync(selectedStudent);
            }
        }
    }
}

下一个组件应该是用于显示选择的学生的组件:
@using BlazorAnswers.Models

@if (SelectedStudent is not null)
{
    @StudentContent(SelectedStudent)
}

@code {
    [Parameter] public StudentModel? SelectedStudent { get; set; }

    [Parameter] public RenderFragment<StudentModel> StudentContent { get; set; } = default!;
}

最后,所有内容都将放在可导航的页面Student.razor中:

Student.razor

@page "/student"
@using BlazorAnswers.Models

<StudentSelector OnStudentChanged="HandleStudentSelected"
                 StudentList="@_students">
    <LoadingContent>
        Loading Students...
    </LoadingContent>
</StudentSelector>

@if (_selectedStudent is not null)
{
    <div class="container-lg pt-4">
        <StudentDisplay SelectedStudent="@_selectedStudent">
            <StudentContent Context="student">
                <p> Displaying profile of Id: @student.Id </p>
                <p> Displaying profile of Name: @student.Name </p>
            </StudentContent>
        </StudentDisplay>
    </div>
}

@code {
    private StudentModel _selectedStudent;
    private IEnumerable<StudentModel> _students = Enumerable.Empty<StudentModel>();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await GetStudents();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    
    /// <summary>
    /// Method that will be invoked in the StudentSelector.razor component
    /// </summary>
    private void HandleStudentSelected(StudentModel selectedStudent)
    {
        _selectedStudent = selectedStudent;
    }
    
    /// <summary>
    /// Method for retrieving students and is intentionally delayed
    /// </summary>    
    private async Task GetStudents()
    {
        await Task.Delay(3000);

        _students = _selectableStudents;
    }

    /// <summary>
    /// Test Data to use as an example
    /// </summary>
    private static IEnumerable<StudentModel> _selectableStudents = new List<StudentModel>()
    {
        new StudentModel{ Id = 1, Name = "Joe" },
        new StudentModel{ Id = 2, Name = "Amy" },
        new StudentModel{ Id = 3, Name = "Beth" },
        new StudentModel{ Id = 4, Name = "Kevin" },
        new StudentModel{ Id = 5, Name = "Carl" },
        new StudentModel{ Id = 6, Name = "Chad" },
        new StudentModel{ Id = 7, Name = "Bryan" },
        new StudentModel{ Id = 8, Name = "Kelly" },
        new StudentModel{ Id = 9, Name = "Steve" },
        new StudentModel{ Id = 10, Name = "Doug" },
    };
}

那么这里是学生模型类:

StudentModel.cs

public class StudentModel
{
  public int Id { get; set; }
  public string Name { get; set; } = string.Empty;
}

设计 Blazor 应用程序时,还有一件事需要考虑,那就是让组件保持简单、页面保持智能。这意味着在注入服务时要尽可能将它们保留在页面中,而不是让它们进入组件本身。这可以避免很多复杂性,并允许您构建可重用的组件。我知道有人提到了调用 StateHasChanged,但请尽量仅在 UI 中的更改源于非人类交互时使用它。输入图像描述


1
非常感谢您的努力和回答。我迫不及待地想很快检查代码。只是想说谢谢。 - elektra

1

在您发布更多代码之前,这是对您问题的当前答案:

<Student StudentId="StudentId"> </Student>
StudentId 是一个字符串,所以你将它作为一个字符串传递了字面值 "StudentId"。无论你如何更改选择器,StudentId 的值都保持为 "StudentId"。渲染器不会在 Student 上调用 SetParametersAsync(这会调用 StateHasChanged),因此没有重新渲染。
一般来说,除了在几种特定情况下(事件处理程序和单个事件中的多个 UI 更新)外,不要使用 StateHasChanged 强制更新组件。
如果你调用它来强制更新 UI,则代码逻辑几乎肯定是错误的。

这并不是真的。只有在StudentId参数被设置为一个字符串属性时,才会出现这种情况。当Blazor不期望一个字符串时,它足够聪明,可以在没有使用@的情况下引用变量。 - Wes Thompson

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