BlazorIntersectionObserver icon indicating copy to clipboard operation
BlazorIntersectionObserver copied to clipboard

Doesn't work with prerendering

Open Xeevis opened this issue 3 years ago • 3 comments

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", "...");            
        }
    }
}

Xeevis avatar Aug 05 '21 15:08 Xeevis

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

ljbc1994 avatar Aug 05 '21 16:08 ljbc1994

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?

Xeevis avatar Aug 06 '21 07:08 Xeevis

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!

ljbc1994 avatar Sep 04 '21 15:09 ljbc1994