因此,我一直在玩Blazor WebAssembly,我不知道如何解决此问题。基本上,我有一个NavMenu.razor页面,该页面将从JSON文件异步获取NavMenuItem数组。很好现在,我想做的是向下面的foreach循环中的import SwiftUI
import PlaygroundSupport
import Combine
final class Testing: ObservableObject {
@Published var items: [String] = []
init() {}
init(items: [String]) {
self.items = items
}
}
struct SVItem: View {
var value: String
init(value: String) {
print("init SVItem: \(value)")
self.value = value
}
var body: some View {
Text(value)
}
}
struct HSItem: View {
var value: String
init(value: String) {
print("init HSItem: \(value)")
self.value = value
}
var body: some View {
Text(value)
}
}
public struct PlaygroundRootView: View {
@EnvironmentObject var testing: Testing
public init() {}
public var body: some View {
VStack{
Text("ScrollView")
ScrollView(.horizontal) {
HStack() {
ForEach(self.testing.items, id: \.self) { value in
SVItem(value: value)
}
}
.background(Color.red)
}
.frame(height: 50)
.background(Color.blue)
Spacer()
Text("HStack")
HStack {
ForEach(self.testing.items, id: \.self) { value in
HSItem(value: value)
}
}
.frame(height: 30)
.background(Color.red)
Spacer()
Button(action: {
print("APPEND button")
self.testing.items.append("A")
}, label: { Text("APPEND ITEM") })
Spacer()
Button(action: {
print("SET button")
self.testing.items = ["A", "B", "C"]
}, label: { Text("SET ITEMS") })
Spacer()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UIHostingController(
// problem 1
rootView: PlaygroundRootView().environmentObject(Testing())
// problem 2
// rootView: PlaygroundRootView().environmentObject(Testing(items: ["1", "2", "3"]))
)
标记生成的每个锚添加一个事件侦听器。
该循环外部的NavLink(未由async函数填充的NavLink)已成功正确地应用了addSmoothScrollingToNavLinks()函数。其他NavLink则没有。似乎它们还没有加入DOM。
我猜有一些奇怪的比赛条件,但我不知道如何解决。关于如何解决此问题的任何想法?
NavMenu.razor
<NavLink>
index.html (在webassembly.js脚本标签下方)
@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; }
}
}
答案 0 :(得分:1)
这是因为Blazor将不等待df.corr()
完成,并且OnInitializedAsync()
启动后将开始呈现视图。参见source code on 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! } ...
如您所见,OnInitializedAsync
可能会在StateHasChanged()
完成之前开始。由于您是通过OnInitializedAsync()
方法发送HTTP请求的,因此该组件会在您从OnInitializedAsync()
端点获得响应之前进行渲染。
您可以在Blazor(而不是js)中绑定事件,并触发由JavaScript编写的处理程序。例如,如果您使用的是ASP.NET Core 3.1.x(不适用于3.0,请参见PR#14509):
sample-data/navmenuitems.json
其中@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");
}
}
是:
MyFunctions.addSmoothScrollingToNavLinks
答案 1 :(得分:1)
我也在尝试正确使用此模式,在OnInitializedAsync
中调用一些异步服务,然后在OnAfterRenderAsync
上调用一些JavaScript。事实是,javascript调用在那里执行一些js,这取决于服务返回的数据(数据必须在其中才能使所需的行为生效)。
发生的事情是js调用在数据到达之前运行,是否有一种方法可以实现OnInitializedAsync
和OnAfterRenderAsync
的数据之间的这种“依赖关系”?
答案 2 :(得分:1)
不完美,但对我有用, 让你等待下载
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;
}
}