BlazorIntersectionObserver
BlazorIntersectionObserver copied to clipboard
Doesn't work with prerendering
When using ASP.NET Core hosted Blazor WebAssembly app it's possible to make use of server prerendering. With this technique first user request is responded to by server instantaneously with full page html. This is useful for SEO and fast content delivery while app is still downloading in the background. Since prerendering happens only on the server with no connection to the browser, it's invalid to call any JS Interop at that time.
- InvalidOperationException: JavaScript interop calls cannot be issued during server-side prerendering,
- because the page has not yet loaded in the browser. Prerendered components must wrap any JavaScript
- interop calls in conditional logic to ensure those interop calls are not attempted during prerendering.
- Blazor.IntersectionObserver.IntersectionObserverService..ctor(IJSRuntime jsRuntime) in IntersectionObserverService.cs
- 27. this.moduleTask = jsRuntime.InvokeAsync<IJSObjectReference>("import", this.scriptPath).AsTask();
Steps to reproduce the behavior:
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/prerendering-and-integration?view=aspnetcore-5.0&pivots=webassembly
Expected behavior
Import JS module only when component is rendered in the browser.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (module == null)
{
module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "...");
}
}
}
Hey thanks for raising..
Is this coming from the IntersectionObserve
component or a custom component?
We do this here:
https://github.com/ljbc1994/BlazorIntersectionObserver/blob/6c69abe42796f5f6875842bee5ee041b81090abc/src/Blazor.IntersectionObserver/IntersectionObserve.cs#L37
We also have it as a task for invocation:
https://github.com/ljbc1994/BlazorIntersectionObserver/blob/6c69abe42796f5f6875842bee5ee041b81090abc/src/Blazor.IntersectionObserver/IntersectionObserverService.cs#L27
Hello, I've looked at this more closely and in my case it's the IIntersectionObserverService
, which can't be injected directly because of JsInterop call in constructor
@inject IIntersectionObserverService ObserverService
So what I did instead was to inject IServiceProvider
and retrieve this service in OnAfterRender()
private IIntersectionObserverService observerService;
[Inject]
private IServiceProvider ServiceProvider { get; set; }
public IntersectionObserver Observer { get; internal set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
this.observerService ??= this.ServiceProvider.GetRequiredService<IIntersectionObserverService>();
this.Observer = await this.observerService.Observe(this.elementRef, (entries) =>
{
//(...)
});
}
}
Perhaps it would be worth a mention in README?
Thanks for looking into this @Xeevis.
I'll spend some more time checking this out to see if there's anything I can do. If not, I'll add this to the readme.
Thanks!