KSi*_*Sib 4 c# asp.net-core blazor-client-side
所以我一直在玩 Blazor WebAssembly,但我不知道如何解决这个问题。基本上,我有一个 NavMenu.razor 页面,它将从 JSON 文件异步获取 NavMenuItem 数组。这很好用。现在,我想要做的是向从<NavLink>下面的 foreach 循环中的标签生成的每个锚点添加一个事件侦听器。
该循环之外的 NavLink(异步函数未填充的那个)成功地正确应用了 addSmoothScrollingToNavLinks() 函数。其他 NavLink 没有。似乎它们还没有在 DOM 中。
我猜有一些奇怪的竞争条件,但我不知道如何解决它。关于我如何解决这个问题的任何想法?
导航菜单.razor
@inject HttpClient Http
@inject IJSRuntime jsRuntime
@if (navMenuItems == null)
{
<div></div>
}
else
{
@foreach (var navMenuItem in navMenuItems)
{
<NavLink class="nav-link" href="@navMenuItem.URL" Match="NavLinkMatch.All">
<div class="d-flex flex-column align-items-center">
<div>
<i class="@navMenuItem.CssClass fa-2x"></i>
</div>
<div class="mt-1">
<span>@navMenuItem.Text</span>
</div>
</div>
</NavLink>
}
}
<NavLink class="nav-link" href="#" Match="NavLinkMatch.All">
<div class="d-flex flex-column align-items-center">
This works!
</div>
</NavLink>
@code {
private NavMenuItem[] navMenuItems;
protected override async Task OnInitializedAsync()
{
navMenuItems = await Http.GetJsonAsync<NavMenuItem[]>("sample-data/navmenuitems.json");
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await jsRuntime.InvokeVoidAsync("MyFunctions.addSmoothScrollingToNavLinks");
}
}
public class NavMenuItem
{
public string Text { get; set; }
public string URL { get; set; }
public string CssClass { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
index.html(在 webassembly.js 脚本标签下)
<script>
function scrollToTop() {
window.scroll({
behavior: 'smooth',
left: 0,
top: 0
});
}
window.MyFunctions = {
addSmoothScrollingToNavLinks: function () {
let links = document.querySelectorAll("a.nav-link");
console.log(links);
// links only has 1 item in it here. The one not generated from the async method
for (const link of links) {
link.addEventListener('click', function (event) {
event.preventDefault();
scrollToTop();
})
}
}
}
</script>
Run Code Online (Sandbox Code Playgroud)
那是因为 Blazor不会等待OnInitializedAsync()完成,一旦OnInitializedAsync开始就会开始渲染视图。在 GitHub 上查看源代码:
private async Task RunInitAndSetParametersAsync()
{
OnInitialized();
var task = OnInitializedAsync(); // NO await here!
if (task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled)
{
// Call state has changed here so that we render after the sync part of OnInitAsync has run
// and wait for it to finish before we continue. If no async work has been done yet, we want
// to defer calling StateHasChanged up until the first bit of async code happens or until
// the end. Additionally, we want to avoid calling StateHasChanged if no
// async work is to be performed.
StateHasChanged(); // Notify here! (happens before await)
try
{
await task; // await the OnInitializedAsync to complete!
}
...
Run Code Online (Sandbox Code Playgroud)
如您所见,StateHasChanged()可能在OnInitializedAsync()完成之前开始。由于您在OnInitializedAsync()方法中发送 HTTP 请求,因此组件会在您从sample-data/navmenuitems.json端点获得响应之前呈现。
您可以在 Blazor(而不是 js)中绑定事件,并触发由 JavaScript 编写的处理程序。例如,如果您使用的是 ASP.NET Core 3.1.x(不适用于 3.0,请参阅PR#14509):
@foreach (var navMenuItem in navMenuItems)
{
<a class="nav-link" href="@navMenuItem.URL" @onclick="OnClickNavLink" @onclick:preventDefault>
<div class="d-flex flex-column align-items-center">
<div>
<i class="@navMenuItem.CssClass fa-2x"></i>
</div>
<div class="mt-1">
<span>@navMenuItem.Text</span>
</div>
</div>
</a>
}
@code{
...
protected override async Task OnAfterRenderAsync(bool firstRender)
{
//if (firstRender)
//{
// await jsRuntime.InvokeVoidAsync("MyFunctions.addSmoothScrollingToNavLinks");
//}
}
private async Task OnClickNavLink(MouseEventArgs e)
{
Console.WriteLine("click link!");
await jsRuntime.InvokeVoidAsync("MyFunctions.smoothScrolling");
}
}
Run Code Online (Sandbox Code Playgroud)
其中MyFunctions.addSmoothScrollingToNavLinks是:
smoothScrolling:function(){
scrollToTop();
}
Run Code Online (Sandbox Code Playgroud)
小智 6
我也试图让这个模式正确,在OnInitializedAsync那之后调用一些异步服务,在OnAfterRenderAsync. 问题是,javascript 调用是为了执行一些依赖于服务返回的数据的 js(数据必须存在才能使所需的行为生效)。
发生的情况是js调用在数据到达之前运行,有没有办法实现来自 和 的数据之间的这种“依赖关系OnInitializedAsync” OnAfterRenderAsync?
不完美,但适合我,让你等待下载
private bool load = false;
protected override async Task OnInitializedAsync()
{
navMenuItems = await Http.GetJsonAsync<NavMenuItem[]>("sample-data/navmenuitems.json");
load = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (load)
{
await jsRuntime.InvokeVoidAsync("MyFunctions.addSmoothScrollingToNavLinks");
load = false;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6329 次 |
| 最近记录: |