Blazor 滚动到新部分

Jam*_*mes 4 .net c# blazor blazor-server-side

我在 blazor 中有一个列表组件,其中有一个可折叠的创建部分,我希望当您单击以显示创建部分时,它将显示并滚动到创建部分。我的问题是 StateHasChanged() 没有及时更新 UI,因此当 javascript 触发时,我们尝试滚动到的元素还不存在。

显然我可以通过超时来解决这个问题,但是在 Blazor 中执行此操作的更干净的方法是什么?有没有办法说类似await StateUpdated()......

我的代码看起来像这样:

@page "/createlist"

<div>
    <h3>My List</h3>
    <a @onclick="ToggleAdding">
       Create New
    </a>
</div>

<ul>
    @* list existing items... *@
</ul>

@if (IsAdding)
{
    <div id="some-create-id">
        <h3>Add Section</h3>
        @* create controls here *@
    </div>
}

@code {

    public void ToggleAdding()
    {
        IsAdding = !IsAdding;

        // Need something similar to StateHasChanged(), that is awaitable.

        JsRuntime.InvokeVoidAsync("ScrollIn", new object[] { CreateElementId });
    }
}
Run Code Online (Sandbox Code Playgroud)

js 很简单:

ScrollIn = id => {
    let element = document.getElementById(id);
    console.log(!!element ? "SCROLLIN" : "NO FOUND ELE", id);
    if (!!element) { element.scrollIntoView(); }
}
Run Code Online (Sandbox Code Playgroud)

Jas*_*bbs 7

您需要从 进行 JavaScript 调用OnAfterRender(Async)。有关所有详细信息,请参阅ASP.NET Core Razor 组件生命周期。

我实际上会将 JavaScript 简化为:

ScrollElementIntoView = element => element.scrollIntoView();
Run Code Online (Sandbox Code Playgroud)

然后 C# 代码将如下所示,不使用任何硬编码 id:

@page "/scroll-test"
@inject IJSRuntime JsRuntime

<div>
    <h3>My List</h3>
    <a @onclick="ToggleAdding">
        Create New
    </a>
</div>

<ul>
    @foreach (var x in Enumerable.Range(0, 100)) {
        <li>@x</li>
    }
</ul>

@if (IsAdding)
{
    <div @ref="AddSectionRef">
        <h3>Add Section</h3>
    </div>
}

@code {
    bool IsAdding { get; set; }
    ElementReference AddSectionRef { get; set; }
    List<Func<Task>> AfterRenderAsyncJobs = new();

    private void ToggleAdding() {
        IsAdding = !IsAdding;
        AfterRenderAsyncJobs.Add(ScrollToAddSection);
        StateHasChanged();
    }

    private async Task ScrollToAddSection() {
        await JsRuntime.InvokeVoidAsync("ScrollElementIntoView", AddSectionRef);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender) {
        while (AfterRenderAsyncJobs.Any()) {
            var job = AfterRenderAsyncJobs.First();
            AfterRenderAsyncJobs.Remove(job);
            await job.Invoke();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)